/*
* ShareNav - Copyright (c) 2007 Harald Mueller james22 at users dot sourceforge dot net
* Copyright (c) 2010-2012 Jyrki Kuoppala jkpj at users dot sourceforge dot net
* See file COPYING.
*/
package net.sharenav.sharenav.ui;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
//#if polish.api.fileconnection
import javax.microedition.io.file.FileConnection;
//#endif
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Choice;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.List;
import javax.microedition.lcdui.TextField;
//#if polish.android
import de.enough.polish.android.lcdui.AndroidDisplay;
import de.enough.polish.android.lcdui.CanvasBridge;
import de.enough.polish.android.midlet.MidletBridge;
import android.app.Activity;
import android.content.Intent;
import android.content.Context;
import android.graphics.Region;
import android.graphics.Region.Op;
import android.os.Bundle;
import android.os.Looper;
import android.os.PowerManager;
import android.util.FloatMath;
import android.view.Display;
import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.widget.Toast;
//#else
import javax.microedition.lcdui.game.GameCanvas;
//#endif
import javax.microedition.midlet.MIDlet;
import de.enough.polish.util.Locale;
import net.sharenav.gps.Node;
import net.sharenav.gps.Satellite;
import net.sharenav.gps.location.CellIdProvider;
import net.sharenav.gps.location.Compass;
import net.sharenav.gps.location.CompassProducer;
import net.sharenav.gps.location.CompassReceiver;
import net.sharenav.gps.location.GsmCell;
import net.sharenav.gps.location.GetCompass;
import net.sharenav.gps.location.LocationMsgProducer;
import net.sharenav.gps.location.LocationMsgReceiver;
import net.sharenav.gps.location.LocationMsgReceiverList;
import net.sharenav.gps.location.LocationUpdateListener;
import net.sharenav.gps.location.NmeaInput;
import net.sharenav.gps.location.SECellId;
import net.sharenav.gps.location.SirfInput;
//#if polish.api.osm-editing
import net.sharenav.sharenav.data.EditableWay;
//#endif
import net.sharenav.sharenav.data.Configuration;
import net.sharenav.sharenav.data.Gpx;
import net.sharenav.sharenav.data.Legend;
import net.sharenav.sharenav.data.PaintContext;
import net.sharenav.sharenav.data.Position;
import net.sharenav.sharenav.data.PositionMark;
import net.sharenav.sharenav.data.RasterTile;
import net.sharenav.sharenav.data.RoutePositionMark;
import net.sharenav.sharenav.data.SECellLocLogger;
import net.sharenav.sharenav.data.ScreenContext;
import net.sharenav.sharenav.data.TrackPlayer;
import net.sharenav.sharenav.graphics.ImageCollector;
import net.sharenav.sharenav.graphics.Images;
import net.sharenav.sharenav.graphics.Proj2D;
import net.sharenav.sharenav.graphics.Proj3D;
import net.sharenav.sharenav.graphics.ProjFactory;
import net.sharenav.sharenav.graphics.Projection;
import net.sharenav.sharenav.mapdata.DictReader;
import net.sharenav.sharenav.mapdata.QueueDataReader;
import net.sharenav.sharenav.mapdata.QueueDictReader;
import net.sharenav.sharenav.mapdata.Way;
import net.sharenav.sharenav.mapdata.WaySegment;
import net.sharenav.sharenav.names.Names;
import net.sharenav.sharenav.names.Urls;
import net.sharenav.sharenav.routing.RouteConnectionTraces;
import net.sharenav.sharenav.routing.RouteHelpers;
import net.sharenav.sharenav.routing.RouteInstructions;
import net.sharenav.sharenav.routing.RouteLineProducer;
import net.sharenav.sharenav.routing.RouteNode;
import net.sharenav.sharenav.routing.RouteSyntax;
import net.sharenav.sharenav.routing.Routing;
import net.sharenav.sharenav.tile.Tile;
import net.sharenav.sharenav.tile.SingleTile;
import net.sharenav.sharenav.ui.DisplayWindow;
import net.sharenav.midlet.iconmenu.IconActionPerformer;
import net.sharenav.midlet.iconmenu.LayoutElement;
import net.sharenav.midlet.ui.CompletionListener;
import net.sharenav.midlet.util.ImageCache;
import net.sharenav.midlet.util.ImageTools;
import net.sharenav.util.CancelMonitorInterface;
import net.sharenav.util.DateTimeTools;
import net.sharenav.util.HelperRoutines;
import net.sharenav.util.IntPoint;
import net.sharenav.util.Logger;
import net.sharenav.util.MoreMath;
import net.sharenav.util.ProjMath;
/**
* Implements the main "Map" screen which displays the map, offers track recording etc.
* @author Harald Mueller
*
*/
public class Trace extends KeyCommandCanvas implements LocationMsgReceiver,
//#if polish.android
View.OnTouchListener,
//#endif
CompassReceiver, Runnable , ShareNavDisplayable, CompletionListener, IconActionPerformer {
/** Soft button for exiting the map screen */
protected static final int EXIT_CMD = 1;
protected static final int CONNECT_GPS_CMD = 2;
protected static final int DISCONNECT_GPS_CMD = 3;
protected static final int START_RECORD_CMD = 4;
protected static final int STOP_RECORD_CMD = 5;
protected static final int MANAGE_TRACKS_CMD = 6;
protected static final int SAVE_WAYP_CMD = 7;
protected static final int ENTER_WAYP_CMD = 8;
protected static final int MANAGE_WAYP_CMD = 9;
protected static final int ROUTING_TOGGLE_CMD = 10;
protected static final int CAMERA_CMD = 11;
protected static final int CLEAR_DEST_CMD = 12;
protected static final int SET_DEST_CMD = 13;
protected static final int MAPFEATURES_CMD = 14;
protected static final int RECORDINGS_CMD = 16;
protected static final int ROUTINGS_CMD = 17;
protected static final int OK_CMD =18;
protected static final int BACK_CMD = 19;
protected static final int ZOOM_IN_CMD = 20;
protected static final int ZOOM_OUT_CMD = 21;
protected static final int MANUAL_ROTATION_MODE_CMD = 22;
protected static final int TOGGLE_OVERLAY_CMD = 23;
protected static final int TOGGLE_BACKLIGHT_CMD = 24;
protected static final int TOGGLE_FULLSCREEN_CMD = 25;
protected static final int TOGGLE_MAP_PROJ_CMD = 26;
protected static final int TOGGLE_KEY_LOCK_CMD = 27;
protected static final int TOGGLE_RECORDING_CMD = 28;
protected static final int TOGGLE_RECORDING_SUSP_CMD = 29;
protected static final int RECENTER_GPS_CMD = 30;
protected static final int DATASCREEN_CMD = 31;
protected static final int OVERVIEW_MAP_CMD = 32;
protected static final int RETRIEVE_XML = 33;
protected static final int PAN_LEFT25_CMD = 34;
protected static final int PAN_RIGHT25_CMD = 35;
protected static final int PAN_UP25_CMD = 36;
protected static final int PAN_DOWN25_CMD = 37;
protected static final int PAN_LEFT2_CMD = 38;
protected static final int PAN_RIGHT2_CMD = 39;
protected static final int PAN_UP2_CMD = 40;
protected static final int PAN_DOWN2_CMD = 41;
protected static final int REFRESH_CMD = 42;
protected static final int SEARCH_CMD = 43;
protected static final int TOGGLE_AUDIO_REC = 44;
protected static final int ROUTING_START_CMD = 45;
protected static final int ROUTING_STOP_CMD = 46;
protected static final int ONLINE_INFO_CMD = 47;
protected static final int ROUTING_START_WITH_MODE_SELECT_CMD = 48;
protected static final int RETRIEVE_NODE = 49;
protected static final int ICON_MENU = 50;
protected static final int ABOUT_CMD = 51;
protected static final int SETUP_CMD = 52;
protected static final int SEND_MESSAGE_CMD = 53;
protected static final int SHOW_DEST_CMD = 54;
protected static final int EDIT_ADDR_CMD = 55;
// protected static final int OPEN_URL_CMD = 56;
protected static final int SHOW_PREVIOUS_POSITION_CMD = 57;
protected static final int TOGGLE_GPS_CMD = 58;
protected static final int CELLID_LOCATION_CMD = 59;
protected static final int MANUAL_LOCATION_CMD = 60;
protected static final int EDIT_ENTITY = 61;
protected static final int NORTH_UP_CMD = 62;
protected static final int HELP_ONLINE_TOUCH_CMD = 63;
protected static final int HELP_ONLINE_WIKI_CMD = 64;
protected static final int KEYS_HELP_CMD = 65;
protected static final int ROUTE_TO_FAVORITE_CMD = 66;
protected static final int ROTATE_TRAVEL_MODE_CMD = 67;
protected static final int SAVE_PREDEF_WAYP_CMD = 68;
protected static final int TOUCH_HELP_CMD = 69;
protected static final int CMS_CMD = 70;
protected static final int TOGGLE_UNUSEABLEWAYS_DARKER = 71;
protected static final int HELP_ONLINE_WIKI_ANDROID_CMD = 72;
protected static final int ROUTING_RECALC_CMD = 73;
protected static final int ROUTING_START_WITH_OPTIONAL_MODE_SELECT_CMD = 74;
protected static final int OPEN_MAP_CREDIT_URL = 75;
protected static final int SAVE_ROUTE_AS_GPX = 76;
private final Command [] CMDS = new Command[77];
public static final int DATASCREEN_NONE = 0;
public static final int DATASCREEN_TACHO = 1;
public static final int DATASCREEN_TRIP = 2;
public static final int DATASCREEN_SATS = 3;
public final static int DISTANCE_GENERIC = 1;
public final static int DISTANCE_ALTITUDE = 2;
public final static int DISTANCE_ROAD = 3;
public final static int DISTANCE_AIR = 4;
public final static int DISTANCE_UNKNOWN = 5;
//#if polish.android
//public static MidletBridge midletBridge;
private PowerManager.WakeLock wl = null;
private PowerManager pm = null;
//#endif
// FIXME should be set based on something like pixels per inch value
private final static int DRAGGEDMUCH_THRESHOLD = 24;
// private SirfInput si;
private volatile LocationMsgProducer locationProducer;
private LocationMsgProducer cellIDLocationProducer = null;
private CompassProducer compassProducer = null;
private volatile int compassDirection = 0;
private volatile int compassDeviation = 0;
private volatile int compassDeviated = 0;
// min?, max? for whole screen, mapMin?/mapMax? for map area
public DisplayWindow rootWindow = null;
public DisplayWindow mapWindow = null;
public DisplayWindow rasterWindow = null;
public volatile byte solution = LocationMsgReceiver.STATUS_OFF;
public String solutionStr = Locale.get("solution.Off");
/** Flag if the user requested to be centered to the current GPS position (true)
* or if the user moved the map away from this position (false).
*/
public boolean gpsRecenter = true;
/** Flag if the gps position is not yet valid after recenter request
*/
public volatile boolean gpsRecenterInvalid = true;
/** Flag if the gps position is stale (last known position instead of current) after recenter request
*/
public volatile boolean gpsRecenterStale = true;
/** Flag if the map is autoZoomed
*/
public boolean autoZoomed = true;
/** Flag if we're manually panning, zooming or rotating the map
*/
public boolean mapBrowsing = false;
/** Is touchscreen pointer pressed
*/
public boolean pointerDown = false;
private volatile boolean currentLayoutIsPortrait = true;
private volatile int previousAngle = 0;
private volatile int currentRotation = 0;
private Position pos = new Position(0.0f, 0.0f,
PositionMark.INVALID_ELEVATION, 0.0f, 0.0f, 1,
System.currentTimeMillis());
/**
* this node contains actually RAD coordinates
* although the constructor for Node(lat, lon) requires parameters in DEC format
* - e. g. "new Node(49.328010f, 11.352556f)"
*/
public Node center = new Node(49.328010f, 11.352556f);
public Node gpsNode = new Node(49.328010f, 11.352556f);
public Node routePointCenter = new Node(49.328010f, 11.352556f);
private Node prevPositionNode = null;
// Projection projection;
private final ShareNav parent;
public static TraceLayout tl = null;
private String lastTitleMsg;
private String currentTitleMsg;
private volatile int currentTitleMsgOpenCount = 0;
private volatile int setTitleMsgTimeout = 0;
private String lastTitleMsgClock;
private String currentAlertTitle;
private String currentAlertMessage;
private volatile int currentAlertsOpenCount = 0;
private volatile int setAlertTimeout = 0;
private long lastBackLightOnTime = 0;
private volatile static long lastUserActionTime = 0;
private long collected = 0;
public PaintContext pc;
public PaintContext rasterPc;
public float scale = Configuration.getRealBaseScale();
int showAddons = 0;
/** x position display was touched last time (on pointerPressed() ) */
private static int touchX = 0;
/** y position display was touched last time (on pointerPressed() ) */
private static int touchY = 0;
/** x position display was released last time (on pointerReleased() ) */
private static int touchReleaseX = 0;
/** y position display was released last time (on pointerReleased() ) */
private static int touchReleaseY = 0;
/** center when display was touched last time (on pointerReleased() ) */
private static Node centerPointerPressedN = new Node();
private static Node pickPointStart = new Node();
private static Node pickPointEnd = new Node();
private static Node centerNode = new Node();
/**
* time at which a pointer press occured to determine
* single / double / long taps
*/
private long pressedPointerTime;
/**
* indicates if the next release event is valid or the corresponding pointer pressing has already been handled
*/
private volatile boolean pointerActionDone;
/** timer checking for single tap */
private volatile TimerTask singleTapTimerTask = null;
/** timer checking for long tap */
private volatile TimerTask longTapTimerTask = null;
/** timer for returning to small buttons */
private volatile TimerTask bigButtonTimerTask = null;
/**
* Indicates that there was any drag event since the last pointerPressed
*/
private static volatile boolean pointerDragged = false;
/**
* Indicates that there was a rather far drag event since the last pointerPressed
*/
private static volatile boolean pointerDraggedMuch = false;
/** indicates whether we already are checking for a single tap in the TimerTask */
private static volatile boolean checkingForSingleTap = false;
private final int DOUBLETAP_MAXDELAY = 300;
private final int LONGTAP_DELAY = 1000;
/** Flag if a route is currently being calculated */
public volatile boolean routeCalc = false;
public Tile tiles[] = new Tile[DictReader.NUM_DICT_ZOOMLEVELS];
public volatile boolean baseTilesRead = false;
public Way actualSpeedLimitWay;
public volatile Way actualWay;
public volatile SingleTile actualSingleTile;
private long oldRecalculationTime;
/** List representing the submenu "Recordings" */
private List recordingsMenu = null;
/** Array of command numbers corresponding to the items in recordingsMenu */
private int[] recordingsMenuCmds = null;
/** List representing the submenu "Routing" */
private List routingsMenu = null;
private GuiTacho guiTacho = null;
private GuiTrip guiTrip = null;
private GuiSatellites guiSatellites = null;
private GuiWaypointSave guiWaypointSave = null;
private final GuiWaypointPredefined guiWaypointPredefined = null;
private static TraceIconMenu traceIconMenu = null;
private static GuiDiscover guiDiscover = null;
private static GuiDiscoverIconMenu guiDiscoverIconMenu = null;
private static CMSLayout cmsl = null;
private final static Logger logger = Logger.getInstance(Trace.class, Logger.DEBUG);
//#mdebug info
public static final String statMsg[] = { "no Start1:", "no Start2:",
"to long :", "interrupt:", "checksum :", "no End1 :",
"no End2 :" };
//#enddebug
/**
* Quality of Bluetooth reception, 0..100.
*/
private byte btquality;
private int[] statRecord;
/**
* Current speed from GPS in km/h.
*/
public volatile int speed;
public volatile float fspeed;
/**
* variables for setting course from GPS movement
* TODO: make speed threshold (currently courseMinSpeed)
* user-settable by transfer mode in the style file
* and/or in user menu
*/
// was three until release 0.7; less than three allows
// for course setting even with slow walking, while
// the heuristics filter away erratic courses
// I've even tested with 0.5f with good results --jkpj
private final float courseMinSpeed = 1.5f;
private volatile int prevCourse = -1;
private volatile int secondPrevCourse = -1;
private volatile int thirdPrevCourse = -1;
/**
* Current altitude from GPS in m.
*/
public volatile int altitude;
/**
* Flag if we're speeding
*/
private volatile boolean speeding = false;
private long lastTimeOfSpeedingSound = 0;
private long startTimeOfSpeedingSign = 0;
private int speedingSpeedLimit = 0;
//#if polish.api.finland
private volatile boolean cameraAlert = false;
private long startTimeOfCameraAlert = 0;
//#endif
private volatile boolean nodeAlert = false;
private volatile int alertNodeType = 0;
private long startTimeOfAlertSign = 0;
/**
* Current course from GPS in compass degrees, 0..359.
*/
private int course = 0;
private int coursegps = 0;
// is current course valid for deciding on how to route
private boolean courseValid = false;
public boolean atDest = false;
public volatile boolean movedAwayFromDest = true;
private Names namesThread;
private Urls urlsThread;
private ImageCollector imageCollector;
private QueueDataReader tileReader;
private QueueDictReader dictReader;
private final Runtime runtime = Runtime.getRuntime();
private StringBuffer sbTemp = new StringBuffer();
private long lLastDragTime = 0;
private RoutePositionMark dest = null;
WaySegment waySegment = new WaySegment();
public Vector route = null;
public int gpxToFollow = -1;
public int gpxAsRoute = -1;
private RouteInstructions ri = null;
private boolean running = false;
private static final int CENTERPOS = Graphics.HCENTER | Graphics.VCENTER;
public Gpx gpx;
public GuiGpx guiGpx = null;
public AudioRecorder audioRec;
private static volatile Trace traceInstance = null;
private volatile Routing routeEngine;
/*
private static Font smallBoldFont;
private static int smallBoldFontHeight;
*/
private static Font fontRouteIcon = null;
public boolean manualRotationMode = false;
public Vector locationUpdateListeners;
private Projection panProjection;
private boolean showingTraceIconMenu = false;
private boolean showingSplitSearch = false;
private boolean showingSplitRaster = false;
private boolean showingSplitSetup = false;
private boolean showingSplitCMS = false;
private GuiWaypointPredefinedForm mForm;
private Vector clickableMarkers;
private IntPoint centerP;
private GuiSearch guiSearch;
//#if polish.android
private static final int INVALID_POINTER_ID = -1;
private int pointerId = INVALID_POINTER_ID;
private int mtPointerId = INVALID_POINTER_ID;
private float pinchZoomDistance = 0f;
private float pinchZoomScale = 0;
private float pinchZoomRotation = 0;
private float pinchZoomOrigAngle = 0;
private volatile boolean rotationStarted = false;
private volatile boolean zoomStarted = false;
private CanvasBridge canvas;
//#endif
public class ClickableCoords {
int x;
int y;
int urlIdx;
int phoneIdx;
int nodeID;
}
private Trace() throws Exception {
//#debug
logger.info("init Trace");
rootWindow = new DisplayWindow(0, 0, getWidth(), getHeight(), true, false);
mapWindow = new DisplayWindow(0, 0, getWidth(), getHeight(), true, false);
this.parent = ShareNav.getInstance();
Configuration.setHasPointerEvents(hasPointerEvents());
if (Configuration.getCfgBitState(Configuration.CFGBIT_TMS_BACKGROUND)
|| Configuration.getCfgBitState(Configuration.CFGBIT_TMS_SPLITSCREEN)) {
scale = Configuration.getRasterScale();
}
CMDS[EXIT_CMD] = new Command(Locale.get("generic.Exit")/*Exit*/, Command.EXIT, 2);
CMDS[REFRESH_CMD] = new Command(Locale.get("trace.Refresh")/*Refresh*/, Command.ITEM, 4);
CMDS[SEARCH_CMD] = new Command(Locale.get("generic.Search")/*Search*/, Command.OK, 1);
CMDS[CONNECT_GPS_CMD] = new Command(Locale.get("trace.StartGPS")/*Start GPS*/,Command.ITEM, 2);
CMDS[DISCONNECT_GPS_CMD] = new Command(Locale.get("trace.StopGPS")/*Stop GPS*/,Command.ITEM, 2);
CMDS[TOGGLE_GPS_CMD] = new Command(Locale.get("trace.ToggleGPS")/*Toggle GPS*/,Command.ITEM, 2);
CMDS[START_RECORD_CMD] = new Command(Locale.get("trace.StartRecord")/*Start record*/,Command.ITEM, 4);
CMDS[STOP_RECORD_CMD] = new Command(Locale.get("trace.StopRecord")/*Stop record*/,Command.ITEM, 4);
CMDS[MANAGE_TRACKS_CMD] = new Command(Locale.get("trace.ManageTracks")/*Manage tracks*/,Command.ITEM, 5);
CMDS[SAVE_WAYP_CMD] = new Command(Locale.get("trace.SaveWaypoint")/*Save waypoint*/,Command.ITEM, 7);
CMDS[ENTER_WAYP_CMD] = new Command(Locale.get("trace.EnterWaypoint")/*Enter waypoint*/,Command.ITEM, 7);
CMDS[MANAGE_WAYP_CMD] = new Command(Locale.get("trace.ManageWaypoints")/*Manage waypoints*/,Command.ITEM, 7);
CMDS[ROUTING_TOGGLE_CMD] = new Command(Locale.get("trace.ToggleRouting")/*Toggle routing*/,Command.ITEM, 3);
CMDS[CAMERA_CMD] = new Command(Locale.get("trace.Camera")/*Camera*/,Command.ITEM, 9);
CMDS[CLEAR_DEST_CMD] = new Command(Locale.get("trace.ClearDestination")/*Clear destination*/,Command.ITEM, 10);
CMDS[SET_DEST_CMD] = new Command(Locale.get("trace.AsDestination")/*As destination*/,Command.ITEM, 11);
CMDS[MAPFEATURES_CMD] = new Command(Locale.get("trace.MapFeatures")/*Map Features*/,Command.ITEM, 12);
CMDS[RECORDINGS_CMD] = new Command(Locale.get("trace.Recordings")/*Recordings...*/,Command.ITEM, 4);
CMDS[ROUTINGS_CMD] = new Command(Locale.get("trace.Routing3")/*Routing...*/,Command.ITEM, 3);
CMDS[OK_CMD] = new Command(Locale.get("generic.OK")/*OK*/,Command.OK, 14);
CMDS[BACK_CMD] = new Command(Locale.get("generic.Back")/*Back*/,Command.BACK, 15);
CMDS[ZOOM_IN_CMD] = new Command(Locale.get("trace.ZoomIn")/*Zoom in*/,Command.ITEM, 100);
CMDS[ZOOM_OUT_CMD] = new Command(Locale.get("trace.ZoomOut")/*Zoom out*/,Command.ITEM, 100);
CMDS[MANUAL_ROTATION_MODE_CMD] = new Command(Locale.get("trace.ManualRotation2")/*Manual rotation mode*/,Command.ITEM, 100);
CMDS[TOGGLE_OVERLAY_CMD] = new Command(Locale.get("trace.NextOverlay")/*Next overlay*/,Command.ITEM, 100);
CMDS[TOGGLE_BACKLIGHT_CMD] = new Command(Locale.get("trace.KeepBacklight")/*Keep backlight on/off*/,Command.ITEM, 100);
CMDS[TOGGLE_FULLSCREEN_CMD] = new Command(Locale.get("trace.SwitchToFullscreen")/*Switch to fullscreen*/,Command.ITEM, 100);
CMDS[TOGGLE_MAP_PROJ_CMD] = new Command(Locale.get("trace.NextMapProjection")/*Next map projection*/,Command.ITEM, 100);
CMDS[TOGGLE_KEY_LOCK_CMD] = new Command(Locale.get("trace.ToggleKeylock")/*(De)Activate Keylock*/,Command.ITEM, 100);
CMDS[TOGGLE_RECORDING_CMD] = new Command(Locale.get("trace.ToggleRecording")/*(De)Activate recording*/,Command.ITEM, 100);
CMDS[TOGGLE_RECORDING_SUSP_CMD] = new Command(Locale.get("trace.SuspendRecording")/*Suspend recording*/,Command.ITEM, 100);
CMDS[RECENTER_GPS_CMD] = new Command(Locale.get("trace.RecenterOnGPS")/*Recenter on GPS*/,Command.ITEM, 100);
CMDS[SHOW_DEST_CMD] = new Command(Locale.get("trace.ShowDestination")/*Show destination*/,Command.ITEM, 100);
CMDS[SHOW_PREVIOUS_POSITION_CMD] = new Command(Locale.get("trace.ShowPreviousPosition")/*Previous position*/,Command.ITEM, 100);
CMDS[DATASCREEN_CMD] = new Command(Locale.get("trace.Tacho")/*Tacho*/, Command.ITEM, 15);
CMDS[OVERVIEW_MAP_CMD] = new Command(Locale.get("trace.OverviewFilterMap")/*Overview/Filter map*/, Command.ITEM, 20);
CMDS[RETRIEVE_XML] = new Command(Locale.get("trace.RetrieveXML")/*Retrieve XML*/,Command.ITEM, 200);
CMDS[EDIT_ENTITY] = new Command(Locale.get("traceiconmenu.EditPOI")/*Edit POI*/,Command.ITEM, 200);
CMDS[PAN_LEFT25_CMD] = new Command(Locale.get("trace.left25")/*left 25%*/,Command.ITEM, 100);
CMDS[PAN_RIGHT25_CMD] = new Command(Locale.get("trace.right25")/*right 25%*/,Command.ITEM, 100);
CMDS[PAN_UP25_CMD] = new Command(Locale.get("trace.up25")/*up 25%*/,Command.ITEM, 100);
CMDS[PAN_DOWN25_CMD] = new Command(Locale.get("trace.down25")/*down 25%*/,Command.ITEM, 100);
CMDS[PAN_LEFT2_CMD] = new Command(Locale.get("trace.left2")/*left 2*/,Command.ITEM, 100);
CMDS[PAN_RIGHT2_CMD] = new Command(Locale.get("trace.right2")/*right 2*/,Command.ITEM, 100);
CMDS[PAN_UP2_CMD] = new Command(Locale.get("trace.up2")/*up 2*/,Command.ITEM, 100);
CMDS[PAN_DOWN2_CMD] = new Command(Locale.get("trace.down2")/*down 2*/,Command.ITEM, 100);
CMDS[TOGGLE_AUDIO_REC] = new Command(Locale.get("trace.AudioRecording")/*Audio recording*/,Command.ITEM, 100);
CMDS[ROUTING_START_CMD] = new Command(Locale.get("trace.CalculateRoute")/*Calculate route*/,Command.ITEM, 100);
CMDS[ROUTING_RECALC_CMD] = new Command(Locale.get("trace.ReCalculateRoute")/*Recalculate route*/,Command.ITEM, 100);
CMDS[ROUTING_STOP_CMD] = new Command(Locale.get("trace.StopRouting")/*Stop routing*/,Command.ITEM, 100);
CMDS[ONLINE_INFO_CMD] = new Command(Locale.get("trace.OnlineInfo")/*Online info*/,Command.ITEM, 100);
CMDS[ROUTING_START_WITH_MODE_SELECT_CMD] = new Command(Locale.get("trace.CalculateRoute2")/*Calculate route...*/,Command.ITEM, 100);
CMDS[ROUTING_START_WITH_OPTIONAL_MODE_SELECT_CMD] = new Command(Locale.get("trace.CalculateRoute3")/*Calculate route...*/,Command.ITEM, 100);
CMDS[RETRIEVE_NODE] = new Command(Locale.get("trace.AddPOI")/*Add POI to OSM...*/,Command.ITEM, 100);
CMDS[ICON_MENU] = new Command(Locale.get("trace.Menu")/*Menu*/,Command.OK, 100);
CMDS[SETUP_CMD] = new Command(Locale.get("trace.Setup")/*Setup*/, Command.ITEM, 25);
CMDS[ABOUT_CMD] = new Command(Locale.get("generic.About")/*About*/, Command.ITEM, 30);
//#if polish.api.wmapi
//#if polish.android
CMDS[SEND_MESSAGE_CMD] = new Command(Locale.get("guisendmessage.SendPos")/*Send SMS (map pos)*/,Command.ITEM, 20);
//#else
CMDS[SEND_MESSAGE_CMD] = new Command(Locale.get("trace.SendSMSMapPos")/*Send SMS (map pos)*/,Command.ITEM, 20);
//#endif
//#endif
CMDS[EDIT_ADDR_CMD] = new Command(Locale.get("trace.AddAddrNode")/*Add Addr node*/,Command.ITEM,100);
CMDS[CELLID_LOCATION_CMD] = new Command(Locale.get("trace.CellidLocation")/*Set location from CellID*/,Command.ITEM,100);
CMDS[MANUAL_LOCATION_CMD] = new Command(Locale.get("trace.ManualLocation")/*Set location manually*/,Command.ITEM,100);
CMDS[HELP_ONLINE_TOUCH_CMD] = new Command(Locale.get("guidiscovericonmenu.Touch")/**/,Command.ITEM,100);
CMDS[HELP_ONLINE_WIKI_CMD] = new Command(Locale.get("guidiscovericonmenu.Wiki")/**/,Command.ITEM,100);
//#if polish.android
CMDS[HELP_ONLINE_WIKI_ANDROID_CMD] = new Command(Locale.get("guidiscovericonmenu.AndroidWiki")/**/,Command.ITEM,100);
//#endif
CMDS[KEYS_HELP_CMD] = new Command(Locale.get("guidiscover.KeyShortcuts")/**/,Command.ITEM,100);
CMDS[ROUTE_TO_FAVORITE_CMD] = new Command(Locale.get("guidiscover.KeyShortcuts")/**/,Command.ITEM,100);
CMDS[ROTATE_TRAVEL_MODE_CMD] = new Command(Locale.get("guiroute.TravelBy")/**/,Command.ITEM,100);
CMDS[TOUCH_HELP_CMD] = new Command(Locale.get("trace.touchhelp")/*Touchscreen functions*/,Command.ITEM,100);
CMDS[CMS_CMD] = new Command(Locale.get("trace.Tacho")/*Tacho*/, Command.ITEM, 100);
CMDS[TOGGLE_UNUSEABLEWAYS_DARKER] = new Command(Locale.get("trace.ToggleUnuseableWaysDarker")/*Toggle unuseable ways darker */, Command.ITEM, 100);
CMDS[OPEN_MAP_CREDIT_URL] = new Command(Locale.get("trace.showmapcredit")/*Show map credit/license webpage*/, Command.ITEM, 100);
CMDS[SAVE_ROUTE_AS_GPX] = new Command(Locale.get("traceiconmenu.SaveRouteGpx")/*Save route as GPX*/, Command.ITEM, 100);
addAllCommands();
if (Legend.isValid) {
Configuration.loadKeyShortcuts(gameKeyCommand, singleKeyPressCommand,
repeatableKeyPressCommand, doubleKeyPressCommand, longKeyPressCommand,
nonReleasableKeyPressCommand, CMDS);
}
// FIXME getWidth() && getHeight() might be incorrect at startup on
// some platforms
Configuration.setCanvasSpecificDefaults(getWidth(), getHeight(), ShareNav.isRunningInMicroEmulator());
try {
if (Legend.isValid) {
startup();
}
} catch (Exception e) {
logger.fatal(Locale.get("trace.GotExceptionDuringStartup")/*Got an exception during startup: */ + e.getMessage());
e.printStackTrace();
return;
}
// setTitle("initTrace ready");
locationUpdateListeners = new Vector();
setRasterSplitMode();
traceInstance = this;
}
public void setRasterSplitMode() {
if (Configuration.getCfgBitState(Configuration.CFGBIT_TMS_SPLITSCREEN)
&& !Configuration.getCfgBitState(Configuration.CFGBIT_TMS_BACKGROUND)) {
showingSplitRaster = true;
refreshWindowLayout();
} else {
showingSplitRaster = false;
refreshWindowLayout();
}
}
//#if polish.android
@Override
public boolean onTouch(View view, MotionEvent event) {
// modeled after J2MEPolish source, multitouch added
float x = event.getX();
float y = event.getY();
int truncatedX = (int)x;
int truncatedY = (int)y;
int action = event.getActionMasked();
CanvasBridge.current().requestFocus();
switch(action) {
case MotionEvent.ACTION_DOWN:
CanvasBridge.current().onTouch(view, event);
pointerId = event.getPointerId(0);
mtPointerId = INVALID_POINTER_ID;
return true;
case MotionEvent.ACTION_POINTER_DOWN:
zoomStarted = false;
rotationStarted = false;
pointerDragged = false;
pointerDraggedMuch = false;
mapBrowsing = true;
pinchZoomDistance = dist(event);
pinchZoomRotation = course + angle(event);
pinchZoomOrigAngle = angle(event);
pinchZoomScale = scale;
mtPointerId = event.getPointerId(1);
if (tl.pointerHasDoubleTapAction(touchX, touchY)) {
doubleTap(touchX, touchY);
} else {
if (tl.pointerHasDoubleTapAction(truncatedX, truncatedY)) {
doubleTap(truncatedX, truncatedY);
}
}
pointerActionDone = true;
return true;
case MotionEvent.ACTION_POINTER_UP:
// FIXME don't just assume it's the second, but check it
mtPointerId = INVALID_POINTER_ID;
mapBrowsing = false;
repaint();
return true;
case MotionEvent.ACTION_UP:
CanvasBridge.current().onTouch(view, event);
pointerId = INVALID_POINTER_ID;
mtPointerId = INVALID_POINTER_ID;
mapBrowsing = false;
repaint();
return true;
case MotionEvent.ACTION_MOVE:
final int pointerIndex = event.findPointerIndex(mtPointerId);
int mCount = event.getPointerCount();
// pinch zoom when at map screen but not in other screens
if (imageCollector != null && imageCollector.isRunning() && mCount > 1 && mtPointerId != INVALID_POINTER_ID) {
// possible FIXME should we skip this if we're getting compass readings?
if (angleDiff((int) pinchZoomOrigAngle, (int) angle(event)) > 20) {
rotationStarted = true;
// restore zoom at start of rotation gesture
// to avoid bug is 3450292 on some devices
mtPointerDragged(pinchZoomScale);
}
if (!rotationStarted && ((pinchZoomDistance / dist(event)) > 1.08f || (pinchZoomDistance / dist(event)) < 0.92f)) {
zoomStarted = true;
}
// stop zoom when rotation starts, to avoid bug id 3560292
if (zoomStarted && !rotationStarted) {
mtPointerDragged(pinchZoomDistance / dist(event) * pinchZoomScale);
}
if (rotationStarted) {
mtPointerRotated((360*3 + pinchZoomRotation - angle(event)) % 360);
}
}
CanvasBridge.current().onTouch(view, event);
return true;
default: return view.onTouchEvent(event);
}
}
private float dist(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
private float angle(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
if (event.getPointerId(0) != pointerId) {
x = 0 - x;
y = 0 - y;
}
if (x == 0.0) {
x = (float) 0.000001;
}
float r = MoreMath.atan(y/x) * MoreMath.FAC_RADTODEC;
if (x < 0) {
r = r + 180;
}
return r;
}
//#endif
public Command getCommand(int command) {
return CMDS[command];
}
/**
* Returns the instance of the map screen. If none exists yet,
* a new instance is generated
* @return Reference to singleton instance
*/
public static synchronized Trace getInstance() {
if (traceInstance == null) {
try {
traceInstance = new Trace();
} catch (Exception e) {
logger.exception(Locale.get("trace.FailedToInitialiseMapScreen")/*Failed to initialise Map screen*/, e);
}
}
return traceInstance;
}
public float getGpsLat() {
return pos.latitude;
}
public float getGpsLon() {
return pos.longitude;
}
public void stopCompass() {
if (compassProducer != null) {
compassProducer.close();
}
compassProducer = null;
}
public void startCompass() {
if (Configuration.getCfgBitState(Configuration.CFGBIT_COMPASS_DIRECTION) && compassProducer == null) {
compassProducer = new GetCompass();
if (!compassProducer.init(this)) {
logger.info("Failed to init compass producer");
compassProducer = null;
} else if (!compassProducer.activate(this)) {
logger.info("Failed to activate compass producer");
compassProducer = null;
}
}
}
/**
* Starts the LocationProvider in the background
*/
public void run() {
try {
if (running) {
receiveMessage(Locale.get("trace.GpsStarterRunning")/*GPS starter already running*/);
return;
}
//#debug info
logger.info("start thread init locationprovider");
if (locationProducer != null) {
receiveMessage(Locale.get("trace.LocProvRunning")/*Location provider already running*/);
return;
}
if (Configuration.getLocationProvider() == Configuration.LOCATIONPROVIDER_NONE) {
receiveMessage(Locale.get("trace.NoLocProv")/*"No location provider*/);
return;
}
running=true;
//#if polish.android
Configuration.setCfgBitSavedState(Configuration.CFGBIT_GPS_CONNECTED, true);
currentRotation = getAndroidRotationAngle();
//#endif
currentLayoutIsPortrait = deviceLayoutIsPortrait();
//#if polish.android
previousAngle = getAndroidRotationAngle();
//#else
previousAngle = deviceLayoutIsPortrait() ? 0 : 90;
//#endif
startCompass();
int locprov = Configuration.getLocationProvider();
//#if polish.android
receiveMessage(Locale.get("trace.ConnectTo")/*Connect to */ + Configuration.LOCATIONPROVIDER[locprov]);
//#else
if (locprov == Configuration.LOCATIONPROVIDER_GPSD) {
receiveMessage(Locale.get("trace.ConnectTo")/*Connect to */ + Configuration.LOCATIONPROVIDER[locprov-1]);
} else {
receiveMessage(Locale.get("trace.ConnectTo")/*Connect to */ + Configuration.LOCATIONPROVIDER[locprov]);
}
if (locprov == Configuration.LOCATIONPROVIDER_GPSD-1) {
locprov = Configuration.LOCATIONPROVIDER_GPSD;
}
//#endif
if (Configuration.getCfgBitSavedState(Configuration.CFGBIT_CELLID_STARTUP)) {
// Don't do initial lookup if we're going to start primary cellid location provider anyway
if (Configuration.getLocationProvider() != Configuration.LOCATIONPROVIDER_SECELL || !Configuration.getCfgBitState(Configuration.CFGBIT_AUTO_START_GPS)) {
commandAction(CELLID_LOCATION_CMD);
}
}
switch (locprov) {
case Configuration.LOCATIONPROVIDER_SIRF:
locationProducer = new SirfInput();
break;
case Configuration.LOCATIONPROVIDER_NMEA:
locationProducer = new NmeaInput();
break;
case Configuration.LOCATIONPROVIDER_GPSD:
locationProducer = new NmeaInput();
break;
case Configuration.LOCATIONPROVIDER_SECELL:
if (cellIDLocationProducer != null) {
cellIDLocationProducer.close();
}
locationProducer = new SECellId();
break;
case Configuration.LOCATIONPROVIDER_JSR179:
//#if polish.api.locationapi
try {
String jsr179Version = null;
try {
jsr179Version = System.getProperty("microedition.location.version");
} catch (RuntimeException re) {
// Some phones throw exceptions if trying to access properties that don't
// exist, so we have to catch these and just ignore them.
} catch (Exception e) {
// As above
}
//#if polish.android
// FIXME current (2010-06, 2011-07 (2.2.1)) android j2mepolish doesn't give this info
//#else
if (jsr179Version != null && jsr179Version.length() > 0) {
//#endif
Class jsr179Class = Class.forName("net.sharenav.gps.location.Jsr179Input");
locationProducer = (LocationMsgProducer) jsr179Class.newInstance();
//#if polish.android
//#else
}
//#endif
} catch (ClassNotFoundException cnfe) {
locationDecoderEnd();
logger.exception(Locale.get("trace.NoJSR179Support")/*Your phone does not support JSR179, please use a different location provider*/, cnfe);
running = false;
return;
}
//#else
// keep Eclipse happy
if (true) {
logger.error(Locale.get("trace.JSR179NotCompiledIn")/*JSR179 is not compiled in this version of ShareNav*/);
running = false;
return;
}
//#endif
break;
case Configuration.LOCATIONPROVIDER_ANDROID:
//#if polish.android
try {
Class AndroidLocationInputClass = Class.forName("net.sharenav.gps.location.AndroidLocationInput");
locationProducer = (LocationMsgProducer) AndroidLocationInputClass.newInstance();
} catch (ClassNotFoundException cnfe) {
locationDecoderEnd();
logger.exception(Locale.get("trace.NoAndroidSupport")/*Your phone does not support Android location API, please use a different location provider*/, cnfe);
running = false;
return;
}
//#else
// keep Eclipse happy
if (true) {
logger.error(Locale.get("trace.AndroidNotCompiledIn")/*Location API for Android is not compiled in this version of ShareNav*/);
running = false;
return;
}
//#endif
break;
}
//#if polish.api.fileconnection
/**
* Allow for logging the raw data coming from the gps
*/
String url = Configuration.getGpsRawLoggerUrl();
//logger.error("Raw logging url: " + url);
if (url != null) {
try {
if (Configuration.getGpsRawLoggerEnable()) {
logger.info("Raw Location logging to: " + url);
url += "rawGpsLog" + HelperRoutines.formatSimpleDateNow() + ".txt";
//#if polish.android
de.enough.polish.android.io.Connection logCon = Connector.open(url);
//#else
javax.microedition.io.Connection logCon = Connector.open(url);
//#endif
if (logCon instanceof FileConnection) {
FileConnection fileCon = (FileConnection)logCon;
if (!fileCon.exists()) {
fileCon.create();
}
locationProducer.enableRawLogging(((FileConnection)logCon).openOutputStream());
} else {
logger.info("Raw logging of NMEA is only to filesystem supported");
}
}
/**
* Help out the OpenCellId.org project by gathering and logging
* data of cell ids together with current Gps location. This information
* can then be uploaded to their web site to determine the position of the
* cell towers. It currently only works for SE phones
*/
if (Configuration.getCfgBitState(Configuration.CFGBIT_CELLID_LOGGING)) {
SECellLocLogger secl = new SECellLocLogger();
if (secl.init()) {
locationProducer.addLocationMsgReceiver(secl);
}
}
} catch (IOException ioe) {
logger.exception(Locale.get("trace.CouldntOpenFileForRawLogging")/*Could not open file for raw logging of Gps data*/,ioe);
} catch (SecurityException se) {
logger.error(Locale.get("trace.PermissionWritingDataDenied")/*Permission to write data for NMEA raw logging was denied*/);
}
}
//#endif
if (locationProducer == null) {
logger.error(Locale.get("trace.ChooseDiffLocMethod")/*Your phone does not seem to support this method of location input, please choose a different one*/);
running = false;
return;
}
if (!locationProducer.init(this)) {
logger.info("Failed to initialise location producer");
running = false;
return;
}
if (!locationProducer.activate(this)) {
logger.info("Failed to activate location producer");
running = false;
return;
}
if (Configuration.getCfgBitState(Configuration.CFGBIT_SND_CONNECT)) {
ShareNav.mNoiseMaker.playSound("CONNECT");
}
//#debug debug
logger.debug("rm connect, add disconnect");
removeCommand(CMDS[CONNECT_GPS_CMD]);
addCommand(CMDS[DISCONNECT_GPS_CMD]);
//#debug info
logger.info("end startLocationPovider thread");
// setTitle("lp="+Configuration.getLocationProvider() + " " + Configuration.getBtUrl());
} catch (SecurityException se) {
/**
* The application was not permitted to connect to the required resources
* Not much we can do here other than gracefully shutdown the thread *
*/
} catch (OutOfMemoryError oome) {
logger.fatal(Locale.get("trace.TraceThreadCrashOOM")/*Trace thread crashed as out of memory: */ + oome.getMessage());
oome.printStackTrace();
} catch (Exception e) {
logger.fatal(Locale.get("trace.TraceThreadCrashWith")/*Trace thread crashed unexpectedly with error */ + e.getMessage());
e.printStackTrace();
} catch (Throwable t) {
running = false;
} finally {
running = false;
}
running = false;
}
// add the command only if icon menus are not used
public void addCommand(Command c) {
if (!Configuration.getCfgBitState(Configuration.CFGBIT_ICONMENUS)) {
super.addCommand(c);
}
}
public RoutePositionMark getDest() {
return dest;
}
// remove the command only if icon menus are not used
public void removeCommand(Command c) {
if (!Configuration.getCfgBitState(Configuration.CFGBIT_ICONMENUS)) {
super.removeCommand(c);
}
}
public synchronized void pause() {
logger.debug("Pause application called");
if (imageCollector != null) {
if (! (routeCalc || route != null)) {
logger.debug("Suspending imageCollector");
imageCollector.suspend();
}
}
// don't pause if we're logging GPX or routing
if (locationProducer != null && !gpx.isRecordingTrk() && ! (routeCalc || route != null)) {
logger.debug("Closing locationProducer");
locationProducer.close();
// wait for locationProducer to close
int polling = 0;
while ((locationProducer != null) && (polling < 7)) {
polling++;
try {
wait(200);
} catch (InterruptedException e) {
break;
}
}
if (locationProducer != null) {
logger.error(Locale.get("trace.LocationProducerTookTooLong")/*LocationProducer took too long to close, giving up*/);
}
}
}
public void resumeAfterPause() {
logger.debug("resuming application after pause");
if (imageCollector != null) {
imageCollector.resume();
}
if (Configuration.getCfgBitState(Configuration.CFGBIT_AUTO_START_GPS) && !running && (locationProducer == null)
//#if polish.android
&& Configuration.getCfgBitState(Configuration.CFGBIT_GPS_CONNECTED)
//#endif
) {
Thread thread = new Thread(this,"LocationProducer init");
thread.start();
}
}
public void resume() {
logger.debug("resuming application");
if (imageCollector != null) {
imageCollector.resume();
}
Thread thread = new Thread(this,"LocationProducer init");
thread.start();
}
public void autoRouteRecalculate() {
if ( gpsRecenter
&&
Configuration.getCfgBitState(Configuration.CFGBIT_ROUTE_AUTO_RECALC)
&&
!isZoomedOutTooFarForRouteCalculation()
) {
if (Math.abs(System.currentTimeMillis()-oldRecalculationTime) >= 7000) {
if (Configuration.getCfgBitState(Configuration.CFGBIT_SND_ROUTINGINSTRUCTIONS)) {
ShareNav.mNoiseMaker.playSound(RouteSyntax.getInstance().getRecalculationSound(), (byte) 5, (byte) 1 );
}
//#debug debug
logger.debug("autoRouteRecalculate");
if (Configuration.getCfgBitState(Configuration.CFGBIT_KEEP_ON_ROAD_IN_ROUTE_GUIDANCE) && isGpsConnected()) {
startRouting(gpsNode);
} else {
startRouting(center);
}
// recalculate route
}
}
}
private boolean isZoomedOutTooFarForRouteCalculation() {
// never recalculate when zoomed out so far that not all routable ways are rendered
if (scale > (Legend.lowestTileScaleLevelWithRoutableWays * Configuration.getMaxDetailBoostMultiplier())) {
receiveMessage(Locale.get("trace.ZoomedOutTooFarToCalculateRoute")); /* Zoomed out too far to calculate route */
return true;
}
return false;
}
public boolean isGpsConnected() {
return (locationProducer != null && solution != LocationMsgReceiver.STATUS_OFF);
}
/**
* Adds all commands for a normal menu.
*/
public void addAllCommands() {
addCommand(CMDS[EXIT_CMD]);
addCommand(CMDS[SEARCH_CMD]);
if (isGpsConnected()) {
addCommand(CMDS[DISCONNECT_GPS_CMD]);
} else {
addCommand(CMDS[CONNECT_GPS_CMD]);
}
//TODO Cleanup addCommand(CMDS[MANAGE_TRACKS_CMD]);
//addCommand(CMDS[MAN_WAYP_CMD]);
addCommand(CMDS[ROUTINGS_CMD]);
addCommand(CMDS[RECORDINGS_CMD]);
addCommand(CMDS[MAPFEATURES_CMD]);
addCommand(CMDS[DATASCREEN_CMD]);
addCommand(CMDS[OVERVIEW_MAP_CMD]);
//#if polish.api.online
addCommand(CMDS[ONLINE_INFO_CMD]);
//#if polish.api.osm-editing
addCommand(CMDS[RETRIEVE_XML]);
addCommand(CMDS[EDIT_ENTITY]);
addCommand(CMDS[RETRIEVE_NODE]);
addCommand(CMDS[EDIT_ADDR_CMD]);
//#endif
//#endif
addCommand(CMDS[SETUP_CMD]);
addCommand(CMDS[ABOUT_CMD]);
if (Configuration.getCfgBitState(Configuration.CFGBIT_ICONMENUS)) {
if (!Configuration.getCfgBitState(Configuration.CFGBIT_FULLSCREEN)) {
//#ifndef polish.android
super.addCommand(CMDS[ICON_MENU]);
//#endif
}
}
setCommandListener(this);
}
/**
* This method must remove all commands that were added by addAllCommands().
*/
public void removeAllCommands() {
//setCommandListener(null);
/* Although j2me documentation says removeCommand for a non-attached command is allowed
* this would crash MicroEmulator. Thus we only remove the commands attached.
*/
removeCommand(CMDS[EXIT_CMD]);
removeCommand(CMDS[SEARCH_CMD]);
if (isGpsConnected()) {
removeCommand(CMDS[DISCONNECT_GPS_CMD]);
} else {
removeCommand(CMDS[CONNECT_GPS_CMD]);
}
//TODO Cleanup removeCommand(CMDS[MANAGE_TRACKS_CMD]);
//removeCommand(CMDS[MAN_WAYP_CMD]);
removeCommand(CMDS[MAPFEATURES_CMD]);
removeCommand(CMDS[RECORDINGS_CMD]);
removeCommand(CMDS[ROUTINGS_CMD]);
removeCommand(CMDS[DATASCREEN_CMD]);
removeCommand(CMDS[OVERVIEW_MAP_CMD]);
//#if polish.api.online
removeCommand(CMDS[ONLINE_INFO_CMD]);
//#if polish.api.osm-editing
removeCommand(CMDS[RETRIEVE_XML]);
removeCommand(CMDS[EDIT_ENTITY]);
removeCommand(CMDS[RETRIEVE_NODE]);
removeCommand(CMDS[EDIT_ADDR_CMD]);
//#endif
//#endif
removeCommand(CMDS[SETUP_CMD]);
removeCommand(CMDS[ABOUT_CMD]);
removeCommand(CMDS[CELLID_LOCATION_CMD]);
removeCommand(CMDS[MANUAL_LOCATION_CMD]);
if (Configuration.getCfgBitState(Configuration.CFGBIT_ICONMENUS)) {
if (!Configuration.getCfgBitState(Configuration.CFGBIT_FULLSCREEN)) {
//#ifndef polish.android
super.removeCommand(CMDS[ICON_MENU]);
//#endif
}
}
}
/** Sets the Canvas to fullScreen or windowed mode
* when icon menus are active the Menu command gets removed
* so the Canvas will not unhide the menu bar first when pressing fire (e.g. on SE mobiles)
*/
public void setFullScreenMode(boolean fullScreen) {
if (Configuration.getCfgBitState(Configuration.CFGBIT_ICONMENUS)) {
//#ifndef polish.android
if (fullScreen) {
super.removeCommand(CMDS[ICON_MENU]);
} else
//#endif
{
//#ifndef polish.android
super.addCommand(CMDS[ICON_MENU]);
//#endif
}
}
//#if polish.android
if (fullScreen) {
MidletBridge.instance.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
} else {
MidletBridge.instance.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
//#else
super.setFullScreenMode(fullScreen);
//#endif
}
public void commandAction(int actionId) {
// take care we'll update actualWay
if (actionId == RETRIEVE_XML || actionId == EDIT_ADDR_CMD) {
repaint();
}
//#if polish.android
final int actionToRun = actionId;
// FIXME would be better to use AsyncTask,
// see http://developer.android.com/resources/articles/painless-threading.html
MidletBridge.instance.runOnUiThread(
new Runnable() {
public void run() {
commandAction(CMDS[actionToRun], null);
}
});
//#else
commandAction(CMDS[actionId], null);
//#endif
}
public void commandAction(Command c, Displayable d) {
updateLastUserActionTime();
try {
if((keyboardLocked) && (d != null)) {
// show alert in keypressed() that keyboard is locked
keyPressed(0);
return;
}
if ((c == CMDS[PAN_LEFT25_CMD]) || (c == CMDS[PAN_RIGHT25_CMD])
|| (c == CMDS[PAN_UP25_CMD]) || (c == CMDS[PAN_DOWN25_CMD])
|| (c == CMDS[PAN_LEFT2_CMD]) || (c == CMDS[PAN_RIGHT2_CMD])
|| (c == CMDS[PAN_UP2_CMD]) || (c == CMDS[PAN_DOWN2_CMD])) {
int panX = 0; int panY = 0;
int courseDiff = 0;
int backLightLevelIndexDiff = 0;
if (c == CMDS[PAN_LEFT25_CMD]) {
panX = -25;
} else if (c == CMDS[PAN_RIGHT25_CMD]) {
panX = 25;
} else if (c == CMDS[PAN_UP25_CMD]) {
panY = -25;
} else if (c == CMDS[PAN_DOWN25_CMD]) {
panY = 25;
} else if (c == CMDS[PAN_LEFT2_CMD]) {
if (TrackPlayer.isPlaying) {
TrackPlayer.slower();
} else if (manualRotationMode) {
courseDiff=-5;
} else {
panX = -2;
}
backLightLevelIndexDiff = -1;
} else if (c == CMDS[PAN_RIGHT2_CMD]) {
if (TrackPlayer.isPlaying) {
TrackPlayer.faster();
} else if (manualRotationMode) {
courseDiff=5;
} else {
panX = 2;
}
backLightLevelIndexDiff = 1;
} else if (c == CMDS[PAN_UP2_CMD]) {
if (route!=null && Configuration.getCfgBitState(Configuration.CFGBIT_ROUTE_BROWSING)) {
RouteInstructions.toNextInstruction(1);
} else {
panY = -2;
}
} else if (c == CMDS[PAN_DOWN2_CMD]) {
if (route!=null && Configuration.getCfgBitState(Configuration.CFGBIT_ROUTE_BROWSING)) {
RouteInstructions.toNextInstruction(-1);
} else {
panY = 2;
}
}
if (backLightLevelIndexDiff !=0 && System.currentTimeMillis() < (lastBackLightOnTime + 5000)) {
// turn backlight always on when dimming
Configuration.setCfgBitState(Configuration.CFGBIT_BACKLIGHT_ON, true, false);
lastBackLightOnTime = System.currentTimeMillis();
Configuration.addToBackLightLevel(backLightLevelIndexDiff);
parent.showBackLightLevel();
} else if (imageCollector != null) {
if (Configuration.getCfgBitState(Configuration.CFGBIT_COMPASS_DIRECTION) && compassProducer != null) {
// set compass compassDeviation
if (compassDeviation == 360) {
compassDeviation = 0;
} else {
compassDeviation += courseDiff;
compassDeviation %= 360;
if (compassDeviation < 0) {
compassDeviation += 360;
}
deviateCompass();
course = compassDeviated;
updatePosition();
}
} else {
// manual rotation
if (courseDiff == 360) {
course = 0; //N
} else {
course += courseDiff;
validateCourse();
course %= 360;
if (course < 0) {
course += 360;
}
}
if (panX != 0 || panY != 0) {
gpsRecenter = false;
}
}
imageCollector.getCurrentProjection().pan(center, panX, panY);
}
gpsRecenter = false;
return;
}
if (c == CMDS[EXIT_CMD]) {
// FIXME: This is a workaround. It would be better if recording
// would not be stopped when leaving the map.
if (Legend.isValid && gpx.isRecordingTrk()) {
alert(Locale.get("trace.RecordMode")/*Record Mode*/, Locale.get("trace.PleaseStopRecording")/*Please stop recording before exit.*/ , 2500);
return;
}
if (Legend.isValid) {
pause();
}
parent.exit();
return;
}
if (c == CMDS[START_RECORD_CMD]) {
try {
//#if polish.android
getPersistent();
//#endif
gpx.newTrk(false);
alert(Locale.get("trace.GpsRecording")/*Gps track recording*/, Locale.get("trace.StartingToRecord")/*Starting to record*/, 1250);
} catch (RuntimeException e) {
receiveMessage(e.getMessage());
}
recordingsMenu = null; // refresh recordings menu
return;
}
if (c == CMDS[STOP_RECORD_CMD]) {
gpx.saveTrk(false);
//#if polish.android
leavePersistent();
//#endif
alert(Locale.get("trace.GpsRecording")/*Gps track recording*/, Locale.get("trace.StoppingToRecord")/*Stopping to record*/, 1250);
recordingsMenu = null; // refresh recordings menu
return;
}
if (c == CMDS[MANAGE_TRACKS_CMD]) {
if (gpx.isRecordingTrk() && !gpx.isRecordingTrkSuspended()) {
// FIXME it's not strictly necessary to stop, after there are translation for the pause
// message, change to Locale.get("trace.YouNeedStopPauseRecording")
alert(Locale.get("trace.RecordMode")/*Record Mode*/, Locale.get("trace.YouNeedStopRecording")/*You need to stop recording before managing tracks.*/ , 4000);
return;
}
guiGpx = new GuiGpx(this);
guiGpx.show();
return;
}
if (c == CMDS[REFRESH_CMD]) {
repaint();
return;
}
if (c == CMDS[CONNECT_GPS_CMD]) {
if (locationProducer == null) {
if (TrackPlayer.isPlaying) {
TrackPlayer.getInstance().stop();
alert(Locale.get("trace.Trackplayer")/*Trackplayer*/, Locale.get("trace.PlayingStopped")/*Playing stopped for connecting to GPS*/, 2500);
}
Thread thread = new Thread(this,"LocationProducer init");
thread.start();
}
return;
}
if (c == CMDS[DISCONNECT_GPS_CMD]) {
//#if polish.android
Configuration.setCfgBitSavedState(Configuration.CFGBIT_GPS_CONNECTED, false);
//#endif
if (locationProducer != null) {
locationProducer.close();
}
return;
}
if (c == CMDS[TOGGLE_GPS_CMD]) {
if (isGpsConnected()) {
commandAction(DISCONNECT_GPS_CMD);
} else {
commandAction(CONNECT_GPS_CMD);
}
return;
}
if (c == CMDS[SEARCH_CMD]) {
if (Configuration.getCfgBitState(Configuration.CFGBIT_ICONMENUS_SPLITSCREEN)
&& hasPointerEvents()) {
showingTraceIconMenu = false;
showingSplitCMS = false;
showingSplitSearch = true;
showingSplitRaster = false;
guiSearch = new GuiSearch(this, GuiSearch.ACTION_DEFAULT);
guiSearch.sizeChanged(getWidth(), getHeight());
refreshWindowLayout();
} else {
guiSearch = new GuiSearch(this, GuiSearch.ACTION_DEFAULT);
guiSearch.show();
}
return;
}
if (c == CMDS[ENTER_WAYP_CMD]) {
if (gpx.isLoadingWaypoints()) {
showAlertLoadingWpt();
} else {
GuiWaypointEnter gwpe = new GuiWaypointEnter(this);
gwpe.show();
}
return;
}
if (c == CMDS[MANAGE_WAYP_CMD]) {
if (gpx.isLoadingWaypoints()) {
showAlertLoadingWpt();
} else {
GuiWaypoint gwp = new GuiWaypoint(this);
gwp.show();
}
return;
}
if (c == CMDS[MAPFEATURES_CMD]) {
GuiMapFeatures gmf = new GuiMapFeatures(this);
gmf.show();
repaint();
return;
}
if (c == CMDS[OVERVIEW_MAP_CMD]) {
GuiOverviewElements ovEl = new GuiOverviewElements(this);
ovEl.show();
repaint();
return;
}
//#if polish.api.wmapi
if (c == CMDS[SEND_MESSAGE_CMD]) {
//#if polish.android
Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "message subject");
shareIntent.putExtra(android.content.Intent.EXTRA_TEXT,
"Lat: " + center.radlat * MoreMath.FAC_RADTODEC +
" " +
"Lon: " + center.radlon * MoreMath.FAC_RADTODEC +
// Locale.get("guisendmessage.Lat")/*Lat: */ + center.radlat * MoreMath.FAC_RADTODEC +
// Locale.get("guisendmessage.Lon")/* Lon: */ + center.radlon * MoreMath.FAC_RADTODEC +
" ");
MidletBridge.instance.startActivity(Intent.createChooser(shareIntent, "Pick a Share method"));
//#else
GuiSendMessage sendMsg = new GuiSendMessage(this);
sendMsg.show();
//#endif
repaint();
return;
}
//#endif
if (c == CMDS[RECORDINGS_CMD]) {
if (recordingsMenu == null) {
boolean hasJSR120 = Configuration.hasDeviceJSR120();
int noElements = 5;
//#if polish.api.mmapi
noElements += 2;
//#endif
//#if polish.api.wmapi
if (hasJSR120) {
noElements++;
}
//#endif
int idx = 0;
String[] elements;
if (gpx.isRecordingTrk()) {
noElements++;
elements = new String[noElements];
recordingsMenuCmds = new int[noElements];
recordingsMenuCmds[idx] = STOP_RECORD_CMD;
elements[idx++] = Locale.get("trace.StopGpxTracklog")/*Stop GPX tracklog*/;
if (gpx.isRecordingTrkSuspended()) {
recordingsMenuCmds[idx] = TOGGLE_RECORDING_SUSP_CMD;
elements[idx++] = Locale.get("trace.ResumeRecording")/*Resume recording*/;
} else {
recordingsMenuCmds[idx] = TOGGLE_RECORDING_SUSP_CMD;
elements[idx++] = Locale.get("trace.SuspendRecording")/*Suspend recording*/;
}
} else {
elements = new String[noElements];
recordingsMenuCmds = new int[noElements];
recordingsMenuCmds[idx] = START_RECORD_CMD;
elements[idx++] = Locale.get("trace.StartGpxTracklog")/*Start GPX tracklog*/;
}
recordingsMenuCmds[idx] = SAVE_WAYP_CMD;
elements[idx++] = Locale.get("trace.SaveWaypoint")/*Save waypoint*/;
recordingsMenuCmds[idx] = ENTER_WAYP_CMD;
elements[idx++] = Locale.get("trace.EnterWaypoint")/*Enter waypoint*/;
recordingsMenuCmds[idx] = MANAGE_TRACKS_CMD;
elements[idx++] = Locale.get("trace.ManageTracks")/*Manage tracks*/;
recordingsMenuCmds[idx] = MANAGE_WAYP_CMD;
elements[idx++] = Locale.get("trace.ManageWaypoints")/*Manage waypoints*/;
//#if polish.api.mmapi
recordingsMenuCmds[idx] = CAMERA_CMD;
elements[idx++] = Locale.get("trace.TakePictures")/*Take pictures*/;
if (audioRec.isRecording()) {
recordingsMenuCmds[idx] = TOGGLE_AUDIO_REC;
elements[idx++] = Locale.get("trace.StopAudioRecording")/*Stop audio recording*/;
} else {
recordingsMenuCmds[idx] = TOGGLE_AUDIO_REC;
elements[idx++] = Locale.get("trace.StartAudioRecording")/*Start audio recording*/;
}
//#endif
//#if polish.api.wmapi
if (hasJSR120) {
recordingsMenuCmds[idx] = SEND_MESSAGE_CMD;
//#if polish.android
elements[idx++] = Locale.get("guisendmessage.SendPos")/*Send SMS (map pos)*/;
//#else
elements[idx++] = Locale.get("trace.SendSMSMapPos")/*Send SMS (map pos)*/;
//#endif
}
//#endif
recordingsMenu = new List(Locale.get("trace.Recordings")/*Recordings...*/,
Choice.IMPLICIT, elements, null);
recordingsMenu.addCommand(CMDS[OK_CMD]);
recordingsMenu.addCommand(CMDS[BACK_CMD]);
recordingsMenu.setSelectCommand(CMDS[OK_CMD]);
parent.show(recordingsMenu);
recordingsMenu.setCommandListener(this);
}
if (recordingsMenu != null) {
recordingsMenu.setSelectedIndex(0, true);
parent.show(recordingsMenu);
}
return;
}
if (c == CMDS[ROUTINGS_CMD]) {
if (routingsMenu == null) {
String[] elements = new String[4];
if (routeCalc || route != null) {
elements[0] = Locale.get("trace.StopRouting")/*Stop routing*/;
} else {
elements[0] = Locale.get("trace.CalculateRoute")/*Calculate route*/;
}
elements[1] = Locale.get("trace.SetDestination")/*Set destination*/;
elements[2] = Locale.get("trace.ShowDestination")/*Show destination*/;
elements[3] = Locale.get("trace.ClearDestination")/*Clear destination*/;
routingsMenu = new List(Locale.get("trace.Routing2")/*Routing..*/, Choice.IMPLICIT, elements, null);
routingsMenu.addCommand(CMDS[OK_CMD]);
routingsMenu.addCommand(CMDS[BACK_CMD]);
routingsMenu.setSelectCommand(CMDS[OK_CMD]);
routingsMenu.setCommandListener(this);
}
if (routingsMenu != null) {
routingsMenu.setSelectedIndex(0, true);
parent.show(routingsMenu);
}
return;
}
if (c == CMDS[SAVE_WAYP_CMD]) {
if (gpx.isLoadingWaypoints()) {
showAlertLoadingWpt();
} else {
PositionMark posMark = getPosMark();
/*
if (Configuration.getCfgBitState(Configuration.CFGBIT_WAYPT_OFFER_PREDEF)) {
if (guiWaypointPredefined == null) {
guiWaypointPredefined = new GuiWaypointPredefined(this);
}
if (guiWaypointPredefined != null) {
guiWaypointPredefined.setData(posMark);
guiWaypointPredefined.show();
}
} else {
showGuiWaypointSave(posMark);
}
*/
showGuiWaypointSave(posMark);
}
return;
}
if (c == CMDS[ONLINE_INFO_CMD] && internetAccessAllowed()) {
contextMenu();
}
if (c == CMDS[BACK_CMD]) {
show();
return;
}
if (c == CMDS[OK_CMD]) {
if (d == recordingsMenu) {
int recCmd = recordingsMenu.getSelectedIndex();
if (recCmd >= 0 && recCmd < recordingsMenuCmds.length) {
recCmd = recordingsMenuCmds[recCmd];
if (recCmd == STOP_RECORD_CMD) {
commandAction(STOP_RECORD_CMD);
if (! Configuration.getCfgBitState(Configuration.CFGBIT_GPX_ASK_TRACKNAME_STOP)) {
show();
}
} else if (recCmd == START_RECORD_CMD) {
commandAction(START_RECORD_CMD);
if (! Configuration.getCfgBitState(Configuration.CFGBIT_GPX_ASK_TRACKNAME_START)) {
show();
}
} else if (recCmd == TOGGLE_RECORDING_SUSP_CMD) {
commandAction(TOGGLE_RECORDING_SUSP_CMD);
show();
} else {
commandAction(recCmd);
}
}
} else if (d == routingsMenu) {
show();
switch (routingsMenu.getSelectedIndex()) {
case 0: {
if (routeCalc || route != null) {
commandAction(ROUTING_STOP_CMD);
} else {
commandAction(ROUTING_START_WITH_OPTIONAL_MODE_SELECT_CMD);
}
break;
}
case 1: {
commandAction(SET_DEST_CMD);
break;
}
case 2: {
commandAction(SHOW_DEST_CMD);
break;
}
case 3: {
commandAction(CLEAR_DEST_CMD);
break;
}
}
}
return;
}
//#if polish.api.mmapi
if (c == CMDS[CAMERA_CMD]) {
try {
Class GuiCameraClass = Class.forName("net.sharenav.sharenav.ui.GuiCamera");
Object GuiCameraObject = GuiCameraClass.newInstance();
GuiCameraInterface cam = (GuiCameraInterface)GuiCameraObject;
cam.init(this);
cam.show();
} catch (ClassNotFoundException cnfe) {
logger.exception(Locale.get("trace.YourPhoneNoCamSupport")/*Your phone does not support the necessary JSRs to use the camera*/, cnfe);
}
return;
}
if (c == CMDS[TOGGLE_AUDIO_REC]) {
if (audioRec.isRecording()) {
audioRec.stopRecord();
} else {
audioRec.startRecorder();
}
recordingsMenu = null; // Refresh recordings menu
return;
}
//#endif
if (c == CMDS[ROUTING_TOGGLE_CMD]) {
if (routeCalc || route != null) {
commandAction(ROUTING_STOP_CMD);
} else {
commandAction(ROUTING_START_WITH_OPTIONAL_MODE_SELECT_CMD);
}
return;
}
if (c == CMDS[ROUTING_START_WITH_MODE_SELECT_CMD]) {
gpsRecenter = true;
gpsRecenterInvalid = true;
gpsRecenterStale = true;
GuiRoute guiRoute = new GuiRoute(this, false);
guiRoute.show();
return;
}
if (c == CMDS[ROUTING_START_WITH_OPTIONAL_MODE_SELECT_CMD]) {
gpsRecenter = true;
gpsRecenterInvalid = true;
gpsRecenterStale = true;
if (Configuration.getCfgBitSavedState(Configuration.CFGBIT_DONT_ASK_FOR_ROUTING_OPTIONS)) {
commandAction(ROUTING_START_CMD);
} else {
GuiRoute guiRoute = new GuiRoute(this, false);
guiRoute.show();
}
return;
}
if (c == CMDS[SAVE_ROUTE_AS_GPX]) {
if (route != null) {
RouteInstructions.outputRouteAsGpx(route);
} else {
alert(Locale.get("guidiscovericonmenu.Routing"), Locale.get("trace.NoRoute")/*Route hasn't been calculated*/, 3000);
}
return;
}
if (c == CMDS[ROUTING_START_CMD]) {
startRouting(center);
return;
}
if (c == CMDS[ROUTING_STOP_CMD]) {
stopRouting(true);
return;
}
if (c == CMDS[ROUTING_RECALC_CMD]) {
stopRouting(false);
startRouting(center);
return;
}
if (c == CMDS[ZOOM_IN_CMD]) {
scale = scale / Configuration.getZoomFactor();
autoZoomed = false;
return;
}
if (c == CMDS[ZOOM_OUT_CMD]) {
scale = scale * Configuration.getZoomFactor();
autoZoomed = false;
return;
}
if (c == CMDS[MANUAL_ROTATION_MODE_CMD]) {
manualRotationMode = !manualRotationMode;
if (manualRotationMode) {
if (hasPointerEvents()) {
alert(Locale.get("trace.ManualRotation")/*Manual Rotation*/, Locale.get("trace.ChangeCourse")/*Change course with zoom buttons*/, 3000);
} else {
alert(Locale.get("trace.ManualRotation")/*Manual Rotation*/, Locale.get("trace.ChangeCourseWithLeftRightKeys")/*Change course with left/right keys*/, 3000);
}
} else {
alert(Locale.get("trace.ManualRotation")/*Manual Rotation*/, Locale.get("generic.Off")/*Off*/, 750);
}
return;
}
if (c == CMDS[TOGGLE_OVERLAY_CMD]) {
showAddons++;
repaint();
return;
}
if (c == CMDS[TOGGLE_UNUSEABLEWAYS_DARKER]) {
Configuration.setCfgBitState(Configuration.CFGBIT_DRAW_NON_TRAVELMODE_WAYS_DARKER,
!(Configuration.getCfgBitState(Configuration.CFGBIT_DRAW_NON_TRAVELMODE_WAYS_DARKER)),
false);
newDataReady();
return;
}
if (c == CMDS[TOGGLE_BACKLIGHT_CMD]) {
// toggle Backlight
Configuration.setCfgBitState(Configuration.CFGBIT_BACKLIGHT_ON,
!(Configuration.getCfgBitState(Configuration.CFGBIT_BACKLIGHT_ON)),
false);
lastBackLightOnTime = System.currentTimeMillis();
parent.showBackLightLevel();
return;
}
if (c == CMDS[TOGGLE_FULLSCREEN_CMD]) {
boolean fullScreen = !Configuration.getCfgBitState(Configuration.CFGBIT_FULLSCREEN);
Configuration.setCfgBitState(Configuration.CFGBIT_FULLSCREEN, fullScreen, false);
setFullScreenMode(fullScreen);
return;
}
if (c == CMDS[HELP_ONLINE_TOUCH_CMD] && internetAccessAllowed()) {
GuiWebInfo.openUrl(GuiWebInfo.getStaticUrlForSite(Locale.get("guiwebinfo.helptouch")));
return;
}
if (c == CMDS[HELP_ONLINE_WIKI_CMD] && internetAccessAllowed()) {
GuiWebInfo.openUrl(GuiWebInfo.getStaticUrlForSite(Locale.get("guiwebinfo.helpwiki")));
return;
}
//#if polish.android
if (c == CMDS[HELP_ONLINE_WIKI_ANDROID_CMD] && internetAccessAllowed()) {
GuiWebInfo.openUrl(GuiWebInfo.getStaticUrlForSite(Locale.get("guiwebinfo.helpwikiandroid")));
return;
}
//#endif
if (c == CMDS[OPEN_MAP_CREDIT_URL] && internetAccessAllowed()) {
GuiWebInfo.openUrl(GuiWebInfo.getStaticUrlForSite(Locale.get("trace.showmapcredit")));
return;
}
if (c == CMDS[KEYS_HELP_CMD]) {
GuiKeyShortcuts gks = new GuiKeyShortcuts(this);
gks.show();
return;
}
if (c == CMDS[TOUCH_HELP_CMD]) {
TouchHelper th = new TouchHelper(this);
th.show();
return;
}
if (c == CMDS[NORTH_UP_CMD]) {
course = 0;
invalidateCourse();
alert(Locale.get("trace.ManualRotation"), Locale.get("trace.ManualToNorth"), 750);
return;
}
if (c == CMDS[TOGGLE_MAP_PROJ_CMD]) {
if (manualRotationMode) {
if (Configuration.getCfgBitState(Configuration.CFGBIT_COMPASS_DIRECTION) && compassProducer != null) {
compassDeviation = 0;
} else {
course = 0;
invalidateCourse();
}
alert(Locale.get("trace.ManualRotation"), Locale.get("trace.ManualToNorth"), 750);
} else {
// FIXME rename string to generic
alert(Locale.get("guidiscover.MapProjection")/*Map Projection*/, ProjFactory.nextProj(), 750);
}
// redraw immediately
synchronized (this) {
if (imageCollector != null) {
imageCollector.newDataReady();
}
}
return;
}
if (c == CMDS[TOGGLE_KEY_LOCK_CMD]) {
keyboardLocked = !keyboardLocked;
if (keyboardLocked) {
// show alert that keys are locked
keyPressed(0);
} else {
alert(Locale.get("trace.ShareNav")/*ShareNav*/, hasPointerEvents() ? Locale.get("trace.KeysAndTouchscreenUnlocked")/*Keys and touch screen unlocked*/ : Locale.get("trace.KeysUnlocked")/*Keys unlocked*/, 1000);
}
return;
}
if (c == CMDS[TOGGLE_RECORDING_CMD]) {
if ( gpx.isRecordingTrk() ) {
commandAction(STOP_RECORD_CMD);
} else {
commandAction(START_RECORD_CMD);
}
return;
}
if (c == CMDS[TOGGLE_RECORDING_SUSP_CMD]) {
if (gpx.isRecordingTrk()) {
if ( gpx.isRecordingTrkSuspended() ) {
alert(Locale.get("trace.GpsRecording")/*Gps track recording*/, Locale.get("trace.ResumingRecording")/*Resuming recording*/, 1000);
gpx.resumeTrk();
} else {
alert(Locale.get("trace.GpsRecording")/*Gps track recording*/, Locale.get("trace.SuspendingRecording")/*Suspending recording*/, 1000);
gpx.suspendTrk();
}
}
recordingsMenu = null; // Refresh recordings menu
return;
}
if (c == CMDS[RECENTER_GPS_CMD]) {
gpsRecenter = true;
gpsRecenterInvalid = true;
gpsRecenterStale = true;
autoZoomed = true;
if (pos.latitude != 0.0f) {
receivePosition(pos);
}
newDataReady();
return;
}
if (c == CMDS[SHOW_DEST_CMD]) {
if (dest != null) {
//We are explicitly setting the map to this position, so we probably don't
//want it to be recentered on the GPS immediately.
gpsRecenter = false;
prevPositionNode = center.copy();
center.setLatLonRad(dest.lat, dest.lon);
movedAwayFromDest = false;
updatePosition();
}
else {
alert(Locale.get("trace.ShowDestination")/*Show destination*/, Locale.get("trace.DestinationNotSpecifiedYet")/*Destination is not specified yet*/, 3000);
}
return;
}
if (c == CMDS[SHOW_PREVIOUS_POSITION_CMD]) {
if (prevPositionNode != null) {
//We are explicitly setting the map to this position, so we probably don't
//want it to be recentered on the GPS immediately.
gpsRecenter = false;
center.setLatLon(prevPositionNode);
updatePosition();
}
return;
}
if (c == CMDS[CMS_CMD]) {
if (Configuration.getCfgBitState(Configuration.CFGBIT_ICONMENUS_SPLITSCREEN)
&& hasPointerEvents()) {
cmsl = new CMSLayout(mapWindow.getMinX(), 0 + rootWindow.getHeight() / 2, rootWindow.getMaxX(), rootWindow.getMaxY());
guiTrip = new GuiTrip();
guiTrip.init();
guiTacho = new GuiTacho();
guiTacho.init();
showingSplitCMS = true;
showingTraceIconMenu = false;
cmsl.setOnScreenButtonSize(false);
//cmsl.sizeChanged(getWidth(), getHeight());
refreshWindowLayout();
}
return;
}
if (c == CMDS[DATASCREEN_CMD]) {
showNextDataScreen(DATASCREEN_NONE);
return;
}
if (c == CMDS[ICON_MENU] && Configuration.getCfgBitState(Configuration.CFGBIT_ICONMENUS)) {
// make markers accessible on keyboard-only phones
if (!hasPointerEvents() && Configuration.getCfgBitState(Configuration.CFGBIT_CLICKABLE_MAPOBJECTS)) {
touchX = pc.getP().getImageCenter().x - imageCollector.xScreenOverscan;
touchY = pc.getP().getImageCenter().y - imageCollector.yScreenOverscan;
}
if (!hasPointerEvents() && Configuration.getCfgBitState(Configuration.CFGBIT_CLICKABLE_MAPOBJECTS) && getClickableMarker(touchX, touchY) != null) {
contextMenu();
} else {
if (isShowingSplitIconMenu()) {
stopShowingSplitScreen();
} else {
showIconMenu();
}
}
return;
}
if (c == CMDS[SETUP_CMD]) {
guiDiscover = new GuiDiscover(parent);
if (isShowingSplitIconMenu()) {
guiDiscoverIconMenu = new GuiDiscoverIconMenu(guiDiscover, guiDiscover);
showingTraceIconMenu = false;
showingSplitSetup = true;
showingSplitCMS = false;
refreshWindowLayout();
guiDiscoverIconMenu.sizeChanged(getWidth(), getHeight());
}
return;
}
if (c == CMDS[ABOUT_CMD]) {
new Splash(parent, ShareNav.initDone);
return;
}
if (c == CMDS[CELLID_LOCATION_CMD]) {
if (Configuration.getLocationProvider() == Configuration.LOCATIONPROVIDER_SECELL && locationProducer != null) {
locationProducer.triggerPositionUpdate();
newDataReady();
} else {
if (cellIDLocationProducer == null) {
// init sleeping cellid location provider if cellid is not primary
cellIDLocationProducer = new SECellId();
if (cellIDLocationProducer != null && !cellIDLocationProducer.init(this)) {
logger.info("Failed to initialise CellID location producer");
}
}
if (cellIDLocationProducer != null) {
cellIDLocationProducer.triggerPositionUpdate();
newDataReady();
}
}
return;
}
if (c == CMDS[MANUAL_LOCATION_CMD]) {
Position setpos = new Position(center.radlat / MoreMath.FAC_DECTORAD,
center.radlon / MoreMath.FAC_DECTORAD,
PositionMark.INVALID_ELEVATION, 0.0f, 0.0f, 1,
System.currentTimeMillis(), Position.TYPE_MANUAL);
// implies center to gps, to give feedback as the gps rectangle
gpsRecenter = true;
// gpsRecenterInvalid = true;
// gpsRecenterStale = true;
autoZoomed = true;
receivePosition(setpos);
receiveStatus(LocationMsgReceiver.STATUS_MANUAL, 0);
newDataReady();
return;
}
if (! routeCalc) {
//#if polish.api.osm-editing
if (c == CMDS[RETRIEVE_XML] && internetAccessAllowed()) {
if (Legend.enableEdits) {
// -1 alert ("Editing", "Urlidx: " + pc.actualWay.urlIdx, Alert.FOREVER);
if ((pc.actualWay != null) && (getUrl(pc.actualWay.urlIdx) != null)) {
parent.alert ("Url", "Url: " + getUrl(pc.actualWay.urlIdx), Alert.FOREVER);
}
if ((actualWay != null) && (actualWay instanceof EditableWay)) {
EditableWay eway = (EditableWay)actualWay;
GuiOsmWayDisplay guiWay = new GuiOsmWayDisplay(eway, actualSingleTile, this);
guiWay.show();
guiWay.refresh();
}
} else {
parent.alert("Editing", "Editing support was not enabled in Osm2ShareNav", Alert.FOREVER);
}
}
if (c == CMDS[EDIT_ENTITY] && internetAccessAllowed()) {
// if we clicked a clickable marker, get coords from the marker instead of tap
int x = centerP.x;
int y = centerP.y;
int nodeID = -1;
int xOverScan = 0;
int yOverScan = 0;
if (imageCollector != null) {
xOverScan = imageCollector.xScreenOverscan;
yOverScan = imageCollector.yScreenOverscan;
panProjection=imageCollector.getCurrentProjection();
} else {
panProjection = null;
}
ClickableCoords coords = getClickableMarker(x - xOverScan, y - yOverScan);
if (coords != null) {
x = coords.x;
y = coords.y;
nodeID = coords.nodeID;
System.out.println("NodeID: " + nodeID);
}
if (Legend.enableEdits) {
// FIXME: do the following:
// * set a flag that default operation is OSM edit
// * do a search for nearby POIs, asking for type
// * when the user selects, open OSM editing
if (nodeID != -1) {
GuiOsmPoiDisplay guiNode = new GuiOsmPoiDisplay(nodeID, null,
center.radlat, center.radlon, this);
guiNode.show();
guiNode.refresh();
return;
} else {
GuiSearch guiSearch = new GuiSearch(this, GuiSearch.ACTION_EDIT_ENTITY);
guiSearch.show();
return;
}
} else {
parent.alert("Editing", "Editing support was not enabled in Osm2ShareNav", Alert.FOREVER);
}
}
if (c == CMDS[RETRIEVE_NODE] && internetAccessAllowed()) {
if (Legend.enableEdits) {
GuiOsmPoiDisplay guiNode = new GuiOsmPoiDisplay(-1, null,
center.radlat, center.radlon, this);
guiNode.show();
guiNode.refresh();
} else {
logger.error(Locale.get("trace.EditingIsNotEnabled")/*Editing is not enabled in this map*/);
}
}
if (c == CMDS[EDIT_ADDR_CMD] && internetAccessAllowed()) {
if (Legend.enableEdits) {
String streetName = "";
//if ((pc != null) && (pc.actualWay != null)) {
// streetName = getName(pc.actualWay.nameIdx);
//}
if (actualWay != null) {
streetName = getName(actualWay.nameIdx);
}
GuiOsmAddrDisplay guiAddr = new GuiOsmAddrDisplay(-1, streetName, null,
center.radlat, center.radlon, this);
guiAddr.show();
} else {
logger.error(Locale.get("trace.EditingIsNotEnabled")/*Editing is not enabled in this map*/);
}
}
//#else
if (c == CMDS[RETRIEVE_XML] || c == CMDS[RETRIEVE_NODE] || c == CMDS[EDIT_ADDR_CMD]) {
alert("No online capabilites",
Locale.get("trace.SetAppGeneric")/*Set app=ShareNav-Generic-editing and enableEditing=true in*/ +
Locale.get("trace.PropertiesFile")/*.properties file and recreate ShareNav with Osm2ShareNav.*/,
Alert.FOREVER);
}
//#endif
if (c == CMDS[SET_DEST_CMD]) {
RoutePositionMark pm1 = new RoutePositionMark(center.radlat, center.radlon);
setDestination(pm1);
return;
}
if (c == CMDS[CLEAR_DEST_CMD]) {
setDestination(null);
return;
}
if (c == CMDS[ROTATE_TRAVEL_MODE_CMD]) {
int mode = Configuration.getTravelModeNr();
mode++;
if (mode >= Legend.getTravelModes().length) {
mode = 0;
}
Configuration.setTravelMode(mode);
if (Configuration.getCfgBitState(Configuration.CFGBIT_DRAW_NON_TRAVELMODE_WAYS_DARKER)) {
newDataReady();
}
return;
}
} else {
alert(Locale.get("trace.Error")/*Error*/, Locale.get("trace.CurrentlyInRouteCalculation")/*Currently in route calculation*/, 2000);
}
} catch (Exception e) {
logger.exception(Locale.get("trace.InTraceCommandAction")/*In Trace.commandAction*/, e);
}
}
private void startRouting(Node center) {
try {
if (isZoomedOutTooFarForRouteCalculation()) {
return;
}
if (!routeCalc || RouteLineProducer.isRunning()) { // if not in route calc or already producing the route line
// if the route line is currently being produced stop it
if (RouteLineProducer.isRunning()) {
RouteInstructions.abortRouteLineProduction();
}
routeCalc = true;
if (Configuration.getContinueMapWhileRouteing() != Configuration.continueMap_Always ) {
stopImageCollector();
}
RouteInstructions.resetOffRoute(route, center);
// center of the map is the route source
RoutePositionMark routeSource = new RoutePositionMark(center.radlat, center.radlon);
logger.info("Routing source: " + routeSource);
routeEngine = new Routing(this);
routeEngine.solve(routeSource, dest);
// resume();
}
routingsMenu = null; // refresh routingsMenu
} catch (Exception e) {
logger.exception(Locale.get("trace.InTraceCommandAction")/*In Trace.commandAction*/, e);
}
}
private ClickableCoords getCenterMarkerCoords() {
// if we clicked a clickable marker, get coords from the marker instead of tap
int x = centerP.x;
int y = centerP.y;
int xOverScan = 0;
int yOverScan = 0;
if (imageCollector != null) {
xOverScan = imageCollector.xScreenOverscan;
yOverScan = imageCollector.yScreenOverscan;
panProjection=imageCollector.getCurrentProjection();
} else {
panProjection = null;
}
return getClickableMarker(x - xOverScan, y - yOverScan);
}
private void contextMenu() {
int x = 0;
int y = 0;
ClickableCoords coords = getCenterMarkerCoords();
String url = null;
String phone = null;
if (coords != null) {
x = coords.x;
y = coords.y;
url = getUrl(coords.urlIdx);
phone = getUrl(coords.phoneIdx);
}
// open a place&marker-related menu
// use the place of touch instead of old center as position,
centerNode=panProjection.inverse(x,
y, centerNode);
Position oPos = new Position(centerNode.radlat, centerNode.radlon,
0.0f, 0.0f, 0.0f, 0, 0);
GuiWebInfo gWeb = new GuiWebInfo(this, oPos, pc, true, coords != null ? url : null,
coords != null ? phone : null,
coords != null ? coords.nodeID : -1);
gWeb.show();
//#if 0
alert(Locale.get("trace.NoOnlineCapa")/*No online capabilites*/,
Locale.get("trace.SetAppGeneric")/*Set app=ShareNav-Generic-editing and enableEditing=true in*/ +
Locale.get("trace.PropertiesFile")/*.properties file and recreate ShareNav with Osm2ShareNav.*/,
Alert.FOREVER);
//#endif
}
private void stopRouting(boolean showAlert) {
gpxToFollow = -1;
gpxAsRoute = -1;
NoiseMaker.stopPlayer();
if (routeCalc) {
if (routeEngine != null) {
routeEngine.cancelRouting();
}
if (showAlert) {
alert(Locale.get("trace.RouteCalculation")/*Route Calculation*/, Locale.get("trace.Cancelled")/*Cancelled*/, 1500);
}
} else {
if (showAlert) {
alert(Locale.get("trace.Routing")/*Routing*/, Locale.get("generic.Off")/*Off*/, 750);
}
}
endRouting();
routingsMenu = null; // refresh routingsMenu
// redraw immediately
synchronized (this) {
if (imageCollector != null) {
imageCollector.newDataReady();
}
}
routingsMenu = null; // refresh routingsMenu
}
private void startImageCollector() throws Exception {
//#debug info
logger.info("Starting ImageCollector");
Images images = new Images();
pc = new PaintContext(this, images);
rasterPc = new PaintContext(this, images);
/* move responsibility for overscan to ImageCollector
int w = (this.getMapWidth() * 125) / 100;
int h = (this.getMapHeight() * 125) / 100;
*/
// Ensure that only one image collector runs at the same time.
if ( imageCollector != null ) {
imageCollector.stop();
// alert("FIXME", "Avoided duplicate ImageCollector", 1500);
}
// FIXME pass layout params to imagecollector
//refreshWindowLayout();
//tl = new TraceLayout(mapMinX, mapMinY, mapMaxX, mapMaxY);
// ImageCollector must not be started with 0x0 image size
if (mapWindow.getWidth() <= 0 || mapWindow.getHeight() <= 0) {
refreshWindowLayout(this.getWidth(), this.getHeight());
}
int x = (mapWindow.getWidth() > 0) ? mapWindow.getWidth() : this.getWidth();
int y = (mapWindow.getHeight() > 0) ? mapWindow.getHeight() : this.getHeight();
System.out.println("Starting image colector " + x + " | " + y);
if (rasterWindow == null) {
rasterWindow = new DisplayWindow(0, rootWindow.getHeight()/2, rootWindow.getWidth(), rootWindow.getHeight(), true, false);
}
imageCollector = new ImageCollector(tiles, x, y, this, images);
// projection = ProjFactory.getInstance(center,course, scale, getMapWidth(), getMapHeight());
// pc.setP(projection);
pc.center = center.copy();
pc.scale = scale;
pc.xSize = x;
pc.ySize = y;
}
private void stopImageCollector(){
//#debug info
logger.info("Stopping ImageCollector");
cleanup();
if (imageCollector != null ) {
imageCollector.stop();
imageCollector=null;
}
System.gc();
}
public boolean isTileRequiredByImageCollector(Tile t) {
if (imageCollector != null ) {
ScreenContext sc = imageCollector.getScreenContext();
if (sc != null && t.contain(sc)) {
return true;
}
}
return false;
}
public void startup() throws Exception {
// logger.info("reading Data ...");
namesThread = new Names();
urlsThread = new Urls();
new DictReader(this);
// wait until base tiles are read; otherwise there's apparently
// a race condition triggered on Symbian s60r3 phones, see
// https://sourceforge.net/support/tracker.php?aid=3284022
if (Legend.isValid) {
while (!baseTilesRead) {
try {
Thread.sleep(250);
} catch (InterruptedException e1) {
// nothing to do in that case
}
}
} else {
if (Configuration.usingBuiltinMap()) {
logger.fatal(Locale.get("legend.bigstyleserrtitle")/*Map format error*/);
} else {
logger.fatal(Locale.get("legend.bigstyleserrtitle")/*Map format error*/ + " " + Configuration.getMapUrl());
}
commandAction(SETUP_CMD);
}
if (Configuration.getCfgBitState(Configuration.CFGBIT_AUTO_START_GPS)) {
Thread thread = new Thread(this, "Trace");
thread.start();
}
// logger.info("Create queueDataReader");
tileReader = new QueueDataReader(this);
// logger.info("create imageCollector");
dictReader = new QueueDictReader(this);
this.gpx = new Gpx();
this.audioRec = new AudioRecorder();
setDict(gpx, (byte) DictReader.GPXZOOMLEVEL);
startImageCollector();
resetSize();
// resetSize() does this
// recreateTraceLayout();
}
public void shutdown() {
if (gpx != null) {
// change to "false" to ask for track name if configure to
// currently "true" to not ask for name to ensure fast
// quit & try to avoid loss of data which might result from
// waiting for user to type the name
//#if polish.android
leavePersistent();
//#endif
gpx.saveTrk(true);
}
//#debug debug
logger.debug("Shutdown: stopImageCollector");
stopImageCollector();
if (namesThread != null) {
//#debug debug
logger.debug("Shutdown: namesThread");
namesThread.stop();
namesThread = null;
}
if (urlsThread != null) {
urlsThread.stop();
urlsThread = null;
}
if (dictReader != null) {
//#debug debug
logger.debug("Shutdown: dictReader");
dictReader.shutdown();
dictReader = null;
}
if (tileReader != null) {
//#debug debug
logger.debug("Shutdown: tileReader");
tileReader.shutdown();
tileReader = null;
}
if (locationProducer != null) {
//#debug debug
logger.debug("Shutdown: locationProducer");
locationProducer.close();
}
if (TrackPlayer.isPlaying) {
//#debug debug
logger.debug("Shutdown: TrackPlayer");
TrackPlayer.getInstance().stop();
}
}
public void restart() {
shutdown();
setBaseTilesRead(false);
tiles = new Tile[6];
try {
startup();
} catch (Exception e) {
logger.fatal(Locale.get("trace.GotExceptionDuringStartup")/*Got an exception during startup: */ + e.getMessage());
e.printStackTrace();
return;
}
}
public void restartImageCollector() {
// don't re-half in split-screen mode
// refreshWindowLayout((mapMaxX - mapMinX), (mapMaxY - mapMinY));
updateLastUserActionTime();
if (imageCollector != null) {
stopImageCollector();
try {
startImageCollector();
imageCollector.resume();
imageCollector.newDataReady();
} catch (Exception e) {
logger.exception(Locale.get("trace.CouldNotReinitialiseImageCollector")/*Could not reinitialise Image Collector after size change*/, e);
}
/**
* Recalculate the projection, as it may depends on the size of the screen
*/
updatePosition();
}
recreateTraceLayout();
}
public void sizeChanged(int w, int h) {
updateLastUserActionTime();
if (imageCollector != null) {
logger.info("Size of Canvas changed to " + w + "|" + h);
System.out.println("Size of Canvas changed to " + w + "|" + h);
stopImageCollector();
try {
refreshWindowLayout(w, h);
startImageCollector();
imageCollector.resume();
imageCollector.newDataReady();
} catch (Exception e) {
logger.exception(Locale.get("trace.CouldNotReinitialiseImageCollector")/*Could not reinitialise Image Collector after size change*/, e);
}
/**
* Recalculate the projection, as it may depends on the size of the screen
*/
updatePosition();
}
refreshWindowLayout(w, h);
recreateTraceLayout();
//#if polish.android
int newAngle = getAndroidRotationAngle();
//#else
int newAngle = deviceLayoutIsPortrait() ? 0 : 90;
//#endif
if (newAngle != previousAngle) {
currentLayoutIsPortrait = deviceLayoutIsPortrait();
compassDeviation += (newAngle - previousAngle);
if (compassDeviation < 0) {
compassDeviation += 360;
}
compassDeviation %= 360;
}
previousAngle = newAngle;
if (isShowingSplitIconMenu() && (traceIconMenu != null)) {
traceIconMenu.sizeChanged(w, h);
}
if (isShowingSplitSearch() && (guiSearch != null)) {
guiSearch.sizeChanged(w, h);
}
if (isShowingSplitSetup() && (guiDiscoverIconMenu != null)) {
guiDiscoverIconMenu.sizeChanged(w, h);
}
if (isShowingSplitCMS() && (cmsl != null)) {
cmsl.sizeChanged(w, h);
}
}
//#if polish.android
public int getAndroidRotationAngle() {
int angle = 0;
switch (MidletBridge.instance.getWindowManager().getDefaultDisplay().getRotation()) {
case Surface.ROTATION_0:
angle = 0;
break;
case Surface.ROTATION_90:
angle = 90;
break;
case Surface.ROTATION_180:
angle = 180;
break;
case Surface.ROTATION_270:
angle = 270;
break;
default:
}
return angle;
}
//#endif
private int getMapWidth() {
return mapWindow.getWidth();
}
private int getMapHeight() {
return mapWindow.getHeight();
}
private void refreshWindowLayout(int w, int h) {
rootWindow.setMinX(0);
rootWindow.setMinY(0);
rootWindow.setMaxX(w);
rootWindow.setMaxY(h);
mapWindow.setMinX(0);
mapWindow.setMinY(0);
mapWindow.setMaxX(w);
mapWindow.setYPosition(0);
if (isShowingSplitScreen()) {
if (mapWindow.getMaxY() != h / 2) {
mapWindow.setMaxY(h / 2);
restartImageCollector();
// FIXME this sets position to other windows,
// not map window
// mapWindow.setYPosition(h / 2);
}
if (Configuration.getCfgBitState(Configuration.CFGBIT_TMS_SPLITSCREEN)) {
rasterWindow = new DisplayWindow(0, rootWindow.getHeight()/2, rootWindow.getWidth(), rootWindow.getHeight(), true, false);
}
} else {
if (mapWindow.getMaxY() != h) {
mapWindow.setMaxY(h);
restartImageCollector();
}
}
repaint();
}
// used when splitscreen mode changes
private void refreshWindowLayout() {
refreshWindowLayout(rootWindow.getMaxX(), rootWindow.getMaxY());
}
// check if pointer operation coordinates are for some other function than trace
private boolean coordsForOthers(int x, int y) {
if (keyboardLocked) {
return false;
}
boolean notForTrace = (x > mapWindow.getMaxX());
if (isShowingSplitScreen()) {
if (y > mapWindow.getMaxY() + mapWindow.getYPosition()) {
notForTrace = true;
}
}
return notForTrace;
}
private void updateCMS(Graphics g) {
//LayoutElement e = null;
if (cmsl != null) {
int maxSpeed = 0;
if (actualSpeedLimitWay != null) {
maxSpeed = actualSpeedLimitWay.getMaxSpeed();
}
if (maxSpeed != 0) {
if (Configuration.getCfgBitState(Configuration.CFGBIT_METRIC)) {
cmsl.ele[CMSLayout.SPEEDING_SIGN].setText(Integer.toString(maxSpeed));
} else {
cmsl.ele[CMSLayout.SPEEDING_SIGN].setText(Integer.toString((int)(maxSpeed / 1.609344f + 0.5f)));
}
}
if (guiTrip != null) {
int y = 48;
Position pos = getCurrentPosition();
//y = guiTrip.paintTrip(g, (cmsl.getMaxX() - cmsl.getMinX()), this.getWidth()/2, this.getWidth() / 2, this.getHeight() - 40, y, pos, getDestination(), this);
y = guiTrip.paintTrip(g, this.getWidth() / 4, this.getHeight()/2, this.getWidth() / 2, this.getHeight(), y, pos, getDestination(), this);
guiTrip.calcSun(this);
// Draw sunrise and sunset time
y += 24;
y = guiTrip.paintSun(g, this.getWidth() / 4,
this.getHeight()/2, this.getWidth()/2, this.getHeight(), y);
}
if (guiTacho != null) {
guiTacho.setValues(this, g);
guiTacho.paintTacho(g, rootWindow.getWidth() / 2,
rootWindow.getHeight() / 2,
rootWindow.getWidth(),
rootWindow.getHeight() / 4 * 3, getCurrentPosition());
}
}
}
protected void paint(Graphics g) {
//#debug debug
logger.debug("Drawing Map screen");
if (!Legend.isValid) {
commandAction(SETUP_CMD);
}
try {
int yc = 1;
int la = 18;
getPC();
if (Configuration.getCfgBitState(Configuration.CFGBIT_TMS_SPLITSCREEN)
&& !Configuration.getCfgBitState(Configuration.CFGBIT_TMS_BACKGROUND)) {
getRasterPC();
rasterPc.g = g;
}
//#if polish.api.paintdirect
g.getCanvas().clipRect(0, 0, this.getWidth(), this.getHeight(), Region.Op.REPLACE);
//#endif
// cleans the screen
g.setColor(Legend.COLORS[Legend.COLOR_MAP_BACKGROUND]);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
if (isShowingSplitRaster()) {
RasterTile.drawRasterMap(rasterPc, rasterWindow);
}
pc.g = g;
if (imageCollector != null) {
/*
* When painting we receive a copy of the center coordinates
* where the imageCollector has drawn last
* as we need to base the routing instructions on the information
* determined during the way drawing (e.g. the current routePathConnection)
*/
//#if polish.api.paintdirect
Node drawnCenter = imageCollector.paintDirect(pc);
//#else
Node drawnCenter = imageCollector.paint(pc);
//#endif
if (route != null && ri != null && pc.lineP2 != null && pc.getP() != null/*avoids exception at route calc*/) {
pc.getP().forward(drawnCenter.radlat, drawnCenter.radlon, pc.lineP2);
/*
* we also need to make sure the current way for the real position
* has really been detected by the imageCollector which happens when drawing the ways
* So we check if we just painted an image that did not cover
* the center of the display because it was too far painted off from
* the display center position and in this case we don't give route instructions
* Thus e.g. after leaving a long tunnel without gps fix there will not be given an
* obsolete instruction from inside the tunnel
*/
int maxAllowedMapMoveOffs = Math.min(pc.xSize/2, pc.ySize/2);
if ( Math.abs(pc.lineP2.x - pc.getP().getImageCenter().x) < maxAllowedMapMoveOffs
&&
Math.abs(pc.lineP2.y - pc.getP().getImageCenter().y) < maxAllowedMapMoveOffs
) {
/*
* we need to synchronize the route instructions on the informations determined during way painting
* so we give the route instructions right after drawing the image with the map
* and use the center of the last drawn image for the route instructions
*/
// FIXME > 1000 km distance shown in route
// instructions when there's no GPS location
ri.showRoute(pc, drawnCenter, gpsNode, imageCollector.xScreenOverscan,imageCollector.yScreenOverscan);
}
}
} else {
// show the way bar even if ImageCollector is not running because it's necessary on touch screens to get to the icon menu
tl.ele[TraceLayout.WAYNAME].setText(" ");
}
lLastDragTime = System.currentTimeMillis();
/* Beginning of voice instructions started from overlay code (besides showRoute above)
*/
// Determine if we are at the destination
if (dest != null) {
float distance = ProjMath.getDistance(dest.lat, dest.lon, center.radlat, center.radlon);
atDest = (distance < 25);
if (atDest) {
if (movedAwayFromDest && Configuration.getCfgBitState(Configuration.CFGBIT_SND_DESTREACHED)) {
ShareNav.mNoiseMaker.playSound(RouteSyntax.getInstance().getDestReachedSound(), (byte) 7, (byte) 1);
}
if (movedAwayFromDest
&& Configuration.getCfgBitState(Configuration.CFGBIT_STOP_ROUTING_AT_DESTINATION)) {
// stop routing
if (routeCalc) {
if (routeEngine != null) {
routeEngine.cancelRouting();
}
}
endRouting();
// redraw immediately
synchronized (this) {
if (imageCollector != null) {
imageCollector.newDataReady();
}
}
}
} else if (!movedAwayFromDest) {
movedAwayFromDest = true;
}
}
// determine if we are currently speeding
speeding = false;
int maxSpeed = 0;
// only detect speeding when gpsRecentered and there is a current way
if (gpsRecenter && actualSpeedLimitWay != null) {
maxSpeed = actualSpeedLimitWay.getMaxSpeed();
// check for winter speed limit if configured
if (Configuration.getCfgBitState(Configuration.CFGBIT_MAXSPEED_WINTER)
&& (actualSpeedLimitWay.getMaxSpeedWinter() > 0)) {
maxSpeed = actualSpeedLimitWay.getMaxSpeedWinter();
}
if (maxSpeed != 0 && speed > (maxSpeed + Configuration.getSpeedTolerance()) ) {
speeding = true;
}
}
if (speeding && Configuration.getCfgBitState(Configuration.CFGBIT_SPEEDALERT_SND)) {
// give speeding alert only every 10 seconds
if ( (System.currentTimeMillis() - lastTimeOfSpeedingSound) > 10000 ) {
lastTimeOfSpeedingSound = System.currentTimeMillis();
ShareNav.mNoiseMaker.immediateSound(RouteSyntax.getInstance().getSpeedLimitSound());
}
}
/*
* end of voice instructions started from overlay code
*/
/*
* the final part of the overlay should not give any voice instructions
*/
g.setColor(Legend.COLOR_MAP_TEXT);
switch (showAddons) {
case 1:
yc = showMemory(g, yc, la);
break;
case 2:
yc = showConnectStatistics(g, yc, la);
break;
default:
showAddons = 0;
if (Configuration.getCfgBitState(Configuration.CFGBIT_SHOW_SCALE_BAR)) {
if (pc != null) {
tl.calcScaleBarWidth(pc);
tl.ele[TraceLayout.SCALEBAR].setText(" ");
}
}
setPointOfTheCompass();
}
showMovement(g);
// Show gpx track recording status
LayoutElement eSolution = tl.ele[TraceLayout.SOLUTION];
LayoutElement eRecorded = tl.ele[TraceLayout.RECORDED_COUNT];
if (gpx.isRecordingTrk()) {
// we are recording tracklogs
if (gpx.isRecordingTrkSuspended()) {
eRecorded.setColor(Legend.COLORS[Legend.COLOR_RECORDING_SUSPENDED_TEXT]); // blue
} else {
eRecorded.setColor(Legend.COLORS[Legend.COLOR_RECORDING_ON_TEXT]); // red
}
eRecorded.setText(gpx.getTrkPointCount() + Locale.get("trace.r")/*r*/);
}
if (TrackPlayer.isPlaying) {
eSolution.setText(Locale.get("trace.Replay")/*Replay*/);
} else {
if (locationProducer == null && !(solution == LocationMsgReceiver.STATUS_CELLID ||
solution == LocationMsgReceiver.STATUS_MANUAL)) {
eSolution.setText(Locale.get("solution.Off")/*Off*/);
} else {
if (Configuration.getCfgBitState(Configuration.CFGBIT_SHOW_ACCURACY) && pos.accuracy != Float.NaN && pos.accuracy > 0) {
eSolution.setText(solutionStr + "/" + (int) pos.accuracy);
} else {
eSolution.setText(solutionStr);
}
}
}
LayoutElement e = tl.ele[TraceLayout.CELLID];
// show if we are logging cellIDs
if (SECellLocLogger.isCellIDLogging() > 0) {
if (SECellLocLogger.isCellIDLogging() == 2) {
e.setColor(Legend.COLORS[Legend.COLOR_CELLID_LOG_ON_TEXT]); // yellow
} else {
//Attempting to log, but currently no valid info available
e.setColor(Legend.COLORS[Legend.COLOR_CELLID_LOG_ON_ATTEMPTING_TEXT]); // red
}
e.setText(Locale.get("trace.cellIDs")/*cellIDs*/);
}
// Display tile and paint state
if ( true ) {
sbTemp.setLength(0);
e = tl.ele[TraceLayout.REQUESTED_TILES];
if (tileReader != null && tileReader.getRequestQueueSize() != 0) {
// Display the number of not yet loaded tiles.
sbTemp.append("T ");
sbTemp.append( tileReader.getRequestQueueSize());
// sbTemp.append("/" + scale);
}
if (imageCollector != null && imageCollector.iDrawState != 0 ) {
// Display a + if the image collector prepares the image.
if ( imageCollector.iDrawState == 1 )
sbTemp.append("+");
// Display a * if the image collector is drawing.
else if ( imageCollector.iDrawState == 2 )
sbTemp.append("*");
}
if (sbTemp.length() != 0 ) {
e.setText(sbTemp.toString());
}
}
// show audio recording status
e = tl.ele[TraceLayout.AUDIOREC];
if (audioRec.isRecording()) {
e.setColor(Legend.COLORS[Legend.COLOR_AUDIOREC_TEXT]); // red
e.setText(Locale.get("trace.AudioRec")/*AudioRec*/);
}
if (pc != null) {
showDestination(pc);
}
if (speed > 0 &&
Configuration.getCfgBitState(Configuration.CFGBIT_SHOW_SPEED_IN_MAP)) {
if (Configuration.getCfgBitState(Configuration.CFGBIT_METRIC)) {
tl.ele[TraceLayout.SPEED_CURRENT].setText(" " + Integer.toString(speed) + Locale.get("guitacho.kmh")/* km/h*/);
} else {
tl.ele[TraceLayout.SPEED_CURRENT].setText(" " + Integer.toString((int)(speed / 1.609344f)) + Locale.get("guitacho.mph"));
}
}
if (Configuration.getCfgBitState(Configuration.CFGBIT_SHOW_ALTITUDE_IN_MAP)
&& locationProducer != null
&& LocationMsgReceiverList.isPosValid(solution)
) {
tl.ele[TraceLayout.ALTITUDE].setText(showDistance(altitude, DISTANCE_ALTITUDE));
}
if (dest != null && (route == null || (!RouteLineProducer.isRouteLineProduced() && !RouteLineProducer.isRunning()) )
&& Configuration.getCfgBitState(Configuration.CFGBIT_SHOW_AIR_DISTANCE_IN_MAP)) {
e = Trace.tl.ele[TraceLayout.ROUTE_DISTANCE];
e.setBackgroundColor(Legend.COLORS[Legend.COLOR_RI_DISTANCE_BACKGROUND]);
double distLine = ProjMath.getDistance(center.radlat, center.radlon, dest.lat, dest.lon);
e.setText(Locale.get("trace.Air")/*Air:*/ + showDistance((int) distLine, DISTANCE_AIR));
}
if (Configuration.getCfgBitState(Configuration.CFGBIT_SHOW_TRAVEL_MODE_IN_MAP)) {
e = tl.ele[TraceLayout.TRAVEL_MODE];
e.setText(Configuration.getTravelMode().getName());
}
String timeString = "";
// we need the time to autoswitch day/night mode
if (Configuration.getCfgBitState(Configuration.CFGBIT_NIGHT_MODE_AUTO)
|| (Configuration.getCfgBitState(Configuration.CFGBIT_SHOW_CLOCK_IN_MAP))) {
if (Configuration.getCfgBitState(Configuration.CFGBIT_GPS_TIME)) {
if (pos.gpsTimeMillis != 0) {
timeString = DateTimeTools.getClock(pos.gpsTimeMillis + Configuration.getTimeDiff()*1000*60, true);
} else if (Configuration.getCfgBitState(Configuration.CFGBIT_GPS_TIME_FALLBACK)) {
timeString = DateTimeTools.getClock(System.currentTimeMillis() + Configuration.getTimeDiff()*1000*60, true);
} else {
// we don't know the time, assume it's day
timeString="11:59";
}
} else {
timeString = DateTimeTools.getClock(System.currentTimeMillis() + Configuration.getTimeDiff()*1000*60, true);
}
}
if (Configuration.getCfgBitState(Configuration.CFGBIT_NIGHT_MODE_AUTO)) {
if (guiTrip == null) {
guiTrip = new GuiTrip(this);
}
int clockInt = Integer.parseInt(timeString.substring(0, timeString.indexOf(":"))) * 60
+ Integer.parseInt(timeString.substring(timeString.indexOf(":") + 1));
boolean isSunUp = guiTrip.isSunUp(this, clockInt);
// System.out.println("Time in clockInt: " + clockInt + " isSunUp: " + isSunUp);
if (!isSunUp != Configuration.getCfgBitState(Configuration.CFGBIT_NIGHT_MODE)) {
Configuration.setCfgBitSavedState(Configuration.CFGBIT_NIGHT_MODE, !isSunUp);
Legend.reReadLegend();
recreateTraceLayout();
}
}
if (Configuration.getCfgBitState(Configuration.CFGBIT_SHOW_CLOCK_IN_MAP)) {
e = tl.ele[TraceLayout.CURRENT_TIME]; // e is used *twice* below (also as vRelative)
if (Configuration.getCfgBitState(Configuration.CFGBIT_GPS_TIME)) {
if (pos.gpsTimeMillis != 0) {
e.setText(timeString);
} else if (Configuration.getCfgBitState(Configuration.CFGBIT_GPS_TIME_FALLBACK)) {
e.setText(timeString);
} else {
e.setText(" ");
}
} else {
e.setText(timeString);
}
/*
don't use new Date() - it is very slow on some Nokia devices
currentTime.setTime( new Date( System.currentTimeMillis() ) );
e.setText(
currentTime.get(Calendar.HOUR_OF_DAY) + ":"
+ HelperRoutines.formatInt2(currentTime.get(Calendar.MINUTE)));
*/
// if current time is visible, positioning OFFROUTE above current time will work
tl.ele[TraceLayout.ROUTE_OFFROUTE].setVRelative(e);
if (isShowingSplitCMS()) {
updateCMS(g);
}
}
if (Configuration.getCfgBitState(Configuration.CFGBIT_SHOW_MAP_CREDITS)) {
tl.ele[TraceLayout.MAP_INFO].setText(getMapCreditsText());
}
setAlertSign(Legend.getNodeTypeDesc((short) alertNodeType));
setSpeedingSign(maxSpeed);
if (hasPointerEvents()) {
tl.ele[TraceLayout.ZOOM_IN].setText("+");
tl.ele[TraceLayout.ZOOM_OUT].setText("-");
tl.ele[TraceLayout.RECENTER_GPS].setText("|");
e = tl.ele[TraceLayout.SHOW_DEST];
if (atDest && prevPositionNode != null) {
e.setText("<");
e.setActionID(SHOW_PREVIOUS_POSITION_CMD);
} else {
e.setText(">");
e.setActionID(SHOW_DEST_CMD + (Trace.SET_DEST_CMD << 16) );
}
tl.ele[TraceLayout.RECORDINGS].setText("*");
tl.ele[TraceLayout.SEARCH].setText("_");
}
e = tl.ele[TraceLayout.TITLEBAR];
if (currentTitleMsgOpenCount != 0) {
e.setText(currentTitleMsg);
// setTitleMsgTimeOut can be changed in receiveMessage()
synchronized (this) {
if (setTitleMsgTimeout != 0) {
TimerTask timerT = new TimerTask() {
public synchronized void run() {
currentTitleMsgOpenCount--;
lastTitleMsg = currentTitleMsg;
if (currentTitleMsgOpenCount == 0) {
//#debug debug
logger.debug("clearing title");
repaint();
}
}
};
ShareNav.getTimer().schedule(timerT, setTitleMsgTimeout);
setTitleMsgTimeout = 0;
}
}
}
tl.paint(g);
if (currentAlertsOpenCount > 0) {
showCurrentAlert(g);
}
} catch (Exception e) {
logger.silentexception("Unhandled exception in the paint code", e);
}
if (isShowingSplitTraceIconMenu() && traceIconMenu != null) {
traceIconMenu.paint(g);
}
if (isShowingSplitSearch() && guiSearch != null) {
guiSearch.paint(g);
}
if (isShowingSplitSetup() && guiDiscoverIconMenu != null) {
guiDiscoverIconMenu.paint(g);
}
if (isShowingSplitCMS() && cmsl != null) {
cmsl.paint(g);
}
}
public String getMapCreditsText() {
String credits = Locale.get("trace.mapcredit")/*Map:*/;
boolean added = false;
if (Legend.getMapFlag(Legend.LEGEND_MAPFLAG_SOURCE_OSM_CC_BY_SA)) {
if (added) {
credits = credits + ",";
}
credits = credits + Locale.get("trace.mapcreditOsmCC");
added = true;
}
if (Legend.getMapFlag(Legend.LEGEND_MAPFLAG_SOURCE_OSM_ODBL)) {
if (added) {
credits = credits + ",";
}
credits = credits + Locale.get("trace.mapcreditOsmODbL");
added = true;
}
if (Legend.getMapFlag(Legend.LEGEND_MAPFLAG_SOURCE_FI_LANDSURVEY)) {
if (added) {
credits = credits + ",";
}
credits = credits + Locale.get("trace.mapcreditFiLandSurvey12");
added = true;
}
if (Legend.getMapFlag(Legend.LEGEND_MAPFLAG_SOURCE_FI_DIGIROAD)) {
if (added) {
credits = credits + ",";
}
credits = credits + Locale.get("trace.mapcreditFiDigiroad");
added = true;
}
return credits;
}
public boolean isShowingSplitScreen() {
return showingTraceIconMenu || showingSplitSearch || showingSplitSetup || showingSplitCMS || showingSplitRaster;
}
public static void clearTraceInstance() {
if (!Legend.isValid) {
traceInstance = null;
}
}
public boolean isShowingSplitSetup() {
return showingSplitSetup;
}
public boolean isShowingSplitCMS() {
return showingSplitCMS;
}
public void clearShowingSplitSetup() {
showingSplitSetup = false;
//restartImageCollector();
}
public void setShowingSplitTraceIconMenu() {
showingTraceIconMenu = true;
}
public boolean isShowingSplitIconMenu() {
return showingTraceIconMenu || showingSplitSetup;
}
public boolean isShowingSplitTraceIconMenu() {
return showingTraceIconMenu;
}
public boolean isShowingSplitSearch() {
return showingSplitSearch;
}
public boolean isShowingSplitRaster() {
return showingSplitRaster;
}
public void showGuiWaypointSave(PositionMark posMark) {
if (guiWaypointSave == null) {
guiWaypointSave = new GuiWaypointSave(this);
}
if (guiWaypointSave != null) {
guiWaypointSave.setData(posMark);
guiWaypointSave.show();
}
}
/** Show an alert telling the user that waypoints are not ready yet.
*/
private void showAlertLoadingWpt() {
alert("Way points", "Way points are not ready yet.", 3000);
}
private void showCurrentAlert(Graphics g) {
Font font = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_MEDIUM);
// same font for title
Font titleFont = font;
int fontHeight = font.getHeight();
// add alert title height plus extra space of one line for calculation of alertHeight
int y = titleFont.getHeight() + 2 + fontHeight;
// extra width for alert
int extraWidth = font.charWidth('W');
// alert is at least as wide as alert title
int alertWidth = titleFont.stringWidth(currentAlertTitle);
// width each alert message line must fit in
int maxWidth = getWidth() - extraWidth;
// Two passes: 1st pass calculates placement and necessary size of alert,
// 2nd pass actually does the drawing
for (int i = 0; i <= 1; i++) {
int nextSpaceAt = 0;
int prevSpaceAt = 0;
int start = 0;
// word wrap
do {
int width = 0;
// add word by word until string part is too wide for screen
while (width < maxWidth && nextSpaceAt <= currentAlertMessage.length() ) {
prevSpaceAt = nextSpaceAt;
nextSpaceAt = currentAlertMessage.indexOf(' ', nextSpaceAt);
if (nextSpaceAt == -1) {
nextSpaceAt = currentAlertMessage.length();
}
width = font.substringWidth(currentAlertMessage, start, nextSpaceAt - start);
nextSpaceAt++;
}
nextSpaceAt--;
// Reduce line word by word or if not possible char by char until the
// remaining string part fits to display width
while (width > maxWidth) {
if (prevSpaceAt > start && nextSpaceAt > prevSpaceAt) {
nextSpaceAt = prevSpaceAt;
} else {
nextSpaceAt--;
}
width = font.substringWidth(currentAlertMessage, start, nextSpaceAt - start);
}
// determine maximum alert width
if (alertWidth < width ) {
alertWidth = width;
}
// during the second pass draw the message text lines
if (i==1) {
g.drawSubstring(currentAlertMessage, start, nextSpaceAt - start,
getWidth()/2, y, Graphics.TOP|Graphics.HCENTER);
}
y += fontHeight;
start = nextSpaceAt;
} while (nextSpaceAt < currentAlertMessage.length() );
// at the end of the first pass draw the alert box and the alert title
if (i == 0) {
alertWidth += extraWidth;
int alertHeight = y;
int alertTop = (mapWindow.getHeight() - alertHeight) /2;
//alertHeight += fontHeight/2;
int alertLeft = (getWidth() - alertWidth) / 2;
// alert background color
g.setColor(Legend.COLORS[Legend.COLOR_ALERT_BACKGROUND]);
g.fillRect(alertLeft, alertTop, alertWidth, alertHeight);
// background color for alert title
g.setColor(Legend.COLORS[Legend.COLOR_ALERT_TITLE_BACKGROUND]);
g.fillRect(alertLeft, alertTop, alertWidth, fontHeight + 3);
// alert border
g.setColor(Legend.COLORS[Legend.COLOR_ALERT_BORDER]);
g.setStrokeStyle(Graphics.SOLID);
g.drawRect(alertLeft, alertTop, alertWidth, fontHeight + 3); // title border
g.drawRect(alertLeft, alertTop, alertWidth, alertHeight); // alert border
// draw alert title
y = alertTop + 2; // output position of alert title
g.setFont(titleFont);
g.setColor(Legend.COLORS[Legend.COLOR_ALERT_TEXT]);
g.drawString(currentAlertTitle, getWidth()/2, y , Graphics.TOP|Graphics.HCENTER);
g.setFont(font);
// output alert message 1.5 lines below alert title in the next pass
y += (fontHeight * 3 / 2);
}
} // end for
// setAlertTimeOut can be changed in receiveMessage()
synchronized (this) {
if (setAlertTimeout != 0) {
TimerTask timerT = new TimerTask() {
public synchronized void run() {
currentAlertsOpenCount--;
if (currentAlertsOpenCount == 0) {
//#debug debug
logger.debug("clearing alert");
repaint();
}
}
};
ShareNav.getTimer().schedule(timerT, setAlertTimeout);
setAlertTimeout = 0;
}
}
}
private void setSpeedingSign(int maxSpeed) {
//speeding = true;
if (Configuration.getCfgBitState(Configuration.CFGBIT_SPEEDALERT_VISUAL)
&&
(
speeding
||
(System.currentTimeMillis() - startTimeOfSpeedingSign) < 3000
)
) {
//#if polish.api.finland
String cameraString = "";
if (cameraAlert && startTimeOfCameraAlert == 0) {
startTimeOfCameraAlert = System.currentTimeMillis();
// FIXME get camera sound from config file, requires
// map format change to pass in legend
if (Configuration.getCfgBitState(Configuration.CFGBIT_SPEEDALERT_SND)) {
ShareNav.mNoiseMaker.immediateSound("CAMERA_ALERT");
}
}
if (Configuration.getCfgBitState(Configuration.CFGBIT_SPEEDCAMERA_ALERT)
&& cameraAlert) {
cameraString = Locale.get("trace.cameraText");
}
//#endif
if (speeding) {
speedingSpeedLimit = maxSpeed;
startTimeOfSpeedingSign = System.currentTimeMillis();
}
//#if polish.api.finland
if (Configuration.getCfgBitState(Configuration.CFGBIT_METRIC)) {
tl.ele[TraceLayout.SPEEDING_SIGN].setText(cameraString + Integer.toString(speedingSpeedLimit));
} else {
tl.ele[TraceLayout.SPEEDING_SIGN].setText(cameraString + Integer.toString((int)(speedingSpeedLimit / 1.609344f + 0.5f)));
}
if ((System.currentTimeMillis() - startTimeOfCameraAlert) >= 8000) {
cameraAlert = false;
startTimeOfCameraAlert = 0;
}
//#else
if (Configuration.getCfgBitState(Configuration.CFGBIT_METRIC)) {
tl.ele[TraceLayout.SPEEDING_SIGN].setText(Integer.toString(speedingSpeedLimit));
} else {
tl.ele[TraceLayout.SPEEDING_SIGN].setText(Integer.toString((int)(speedingSpeedLimit / 1.609344f + 0.5f)));
}
//#endif
} else {
startTimeOfSpeedingSign = 0;
//#if polish.api.finland
startTimeOfCameraAlert = 0;
//#endif
}
}
private void setAlertSign(String alert) {
//FIXME use alert sign visual instead, get from legend after
// map format change
if (Configuration.getCfgBitState(Configuration.CFGBIT_NODEALERT_VISUAL)
&&
(
nodeAlert
||
(System.currentTimeMillis() - startTimeOfAlertSign) < 8000)
) {
if (nodeAlert && startTimeOfAlertSign == 0) {
startTimeOfAlertSign = System.currentTimeMillis();
//FIXME get sound from config file, requires
// map format change to pass in legend
if (Configuration.getCfgBitState(Configuration.CFGBIT_NODEALERT_SND)) {
ShareNav.mNoiseMaker.immediateSound("ALERT");
}
}
tl.ele[TraceLayout.SPEEDING_SIGN].setText(alert.substring(0,7));
if ((System.currentTimeMillis() - startTimeOfAlertSign) >= 8000) {
nodeAlert = false;
startTimeOfAlertSign = 0;
}
} else {
startTimeOfAlertSign = 0;
}
}
/**
*
*/
private void getPC() {
pc.course = course;
pc.scale = scale;
pc.center = center.copy();
pc.gpsNode = gpsNode.copy();
// pc.setP( projection);
// projection.inverse(pc.xSize, 0, pc.screenRU);
// projection.inverse(0, pc.ySize, pc.screenLD);
pc.dest = dest;
}
private void getRasterPC() {
rasterPc.course = course;
rasterPc.scale = scale;
rasterPc.center = center.copy();
rasterPc.gpsNode = gpsNode.copy();
// pc.setP( projection);
// projection.inverse(pc.xSize, 0, pc.screenRU);
// projection.inverse(0, pc.ySize, pc.screenLD);
rasterPc.dest = dest;
}
public void cleanup() {
namesThread.cleanup();
urlsThread.cleanup();
tileReader.incUnusedCounter();
dictReader.incUnusedCounter();
}
// public void searchElement(PositionMark pm) throws Exception {
// PaintContext pc = new PaintContext(this, null);
// // take a bigger angle for lon because of positions near to the poles.
// Node nld = new Node(pm.lat - 0.0001f, pm.lon - 0.0005f, true);
// Node nru = new Node(pm.lat + 0.0001f, pm.lon + 0.0005f, true);
// pc.searchLD = nld;
// pc.searchRU = nru;
// pc.dest = pm;
// pc.setP(new Proj2D(new Node(pm.lat, pm.lon, true), 5000, 100, 100));
// for (int i = 0; i < 4; i++) {
// tiles[i].walk(pc, Tile.OPT_WAIT_FOR_LOAD);
// }
// }
public void searchNextRoutableWay(RoutePositionMark pm) throws Exception {
PaintContext pc = new PaintContext(this, null);
// take a bigger angle for lon because of positions near to the pols.
// Node nld=new Node(pm.lat - 0.0001f,pm.lon - 0.0005f,true);
// Node nru=new Node(pm.lat + 0.0001f,pm.lon + 0.0005f,true);
// pc.searchLD=nld;
// pc.searchRU=nru;
pc.squareDstWithPenToActualRoutableWay = Float.MAX_VALUE;
pc.xSize = 100;
pc.ySize = 100;
// retry searching an expanding region at the position mark
Projection p;
do {
p = new Proj2D(new Node(pm.lat,pm.lon, true),5000,pc.xSize,pc.ySize);
pc.setP(p);
for (int i=0; i<4; i++) {
if (Legend.tileScaleLevelContainsRoutableWays[i]) {
tiles[i].walk(pc, Tile.OPT_WAIT_FOR_LOAD | Tile.OPT_FIND_CURRENT);
}
}
// stop the search when a routable way is found
if (pc.actualRoutableWay != null) {
break;
}
// expand the region that gets searched for a routable way
pc.xSize += 100;
pc.ySize += 100;
// System.out.println(pc.xSize);
} while(MoreMath.dist(p.getMinLat(), p.getMinLon(), p.getMinLat(), p.getMaxLon()) < 500); // until we searched at least 500 m edge length
Way w = pc.actualRoutableWay;
pm.setEntity(w, pc.currentPos.nodeLat, pc.currentPos.nodeLon);
}
private void setPointOfTheCompass() {
StringBuffer c = new StringBuffer(5);
if (ProjFactory.getProj() != ProjFactory.NORTH_UP
&& Configuration.getCfgBitState(Configuration.CFGBIT_SHOW_POINT_OF_COMPASS)) {
c.append(Configuration.getCompassDirection(course));
}
// if tl shows big onscreen buttons add spaces to short compass directions
if (tl.bigOnScreenButtons) {
if (ProjFactory.getProj() == ProjFactory.NORTH_UP) {
c.setLength(0);
c.append('(').append(Configuration.getCompassDirection(0)).append(')');
}
while (c.length() <= 3) {
c.insert(0,' ').append(' ');
}
}
tl.ele[TraceLayout.POINT_OF_COMPASS].setText(c.toString());
}
private int showConnectStatistics(Graphics g, int yc, int la) {
g.setColor(Legend.COLORS[Legend.COLOR_MAP_TEXT]);
// only try to show compass id and cell id if user has somehow switched them on
GsmCell cell = null;
if (cellIDLocationProducer != null || Configuration.getLocationProvider() == Configuration.LOCATIONPROVIDER_SECELL || Configuration.getCfgBitState(Configuration.CFGBIT_CELLID_LOGGING)) {
cell = CellIdProvider.getInstance().obtainCachedCellID();
}
Compass compass = null;
if (Configuration.getCfgBitState(Configuration.CFGBIT_COMPASS_DIRECTION)) {
compass = GetCompass.getInstance().obtainCachedCompass();
}
if (cell == null) {
g.drawString("No Cell ID available", 0, yc, Graphics.TOP
| Graphics.LEFT);
yc += la;
} else {
g.drawString("Cell: MCC=" + cell.mcc + " MNC=" + cell.mnc, 0, yc, Graphics.TOP
| Graphics.LEFT);
yc += la;
g.drawString("LAC=" + cell.lac, 0, yc, Graphics.TOP
| Graphics.LEFT);
yc += la;
g.drawString("cellID=" + cell.cellID, 0, yc, Graphics.TOP
| Graphics.LEFT);
yc += la;
}
if (compass == null) {
g.drawString("No compass direction available", 0, yc, Graphics.TOP
| Graphics.LEFT);
yc += la;
} else {
g.drawString("Compass direction: " + compass.direction, 0, yc, Graphics.TOP
| Graphics.LEFT);
yc += la;
}
if (statRecord == null) {
g.drawString("No stats yet", 0, yc, Graphics.TOP
| Graphics.LEFT);
return yc+la;
}
//#mdebug info
for (byte i = 0; i < LocationMsgReceiver.SIRF_FAIL_COUNT; i++) {
g.drawString(statMsg[i] + statRecord[i], 0, yc, Graphics.TOP
| Graphics.LEFT);
yc += la;
}
//#enddebug
g.drawString("BtQual : " + btquality, 0, yc, Graphics.TOP | Graphics.LEFT);
yc += la;
g.drawString("Count : " + collected, 0, yc, Graphics.TOP
| Graphics.LEFT);
yc += la;
return yc;
}
public void showDestination(PaintContext pc) {
// Avoid exception after route calculation
if ( pc.getP() == null || imageCollector == null )
return;
try {
if (dest != null) {
pc.getP().forward(dest.lat, dest.lon, pc.lineP2);
// System.out.println(dest.toString());
int x = pc.lineP2.x - imageCollector.xScreenOverscan;
int y = pc.lineP2.y - imageCollector.yScreenOverscan;
pc.g.drawImage(pc.images.IMG_DEST, x, y, CENTERPOS);
pc.g.setColor(Legend.COLORS[Legend.COLOR_DEST_TEXT]);
if (dest.displayName != null) {
pc.g.drawString(dest.displayName, x, y+8,
Graphics.TOP | Graphics.HCENTER);
}
pc.g.setStrokeStyle(Graphics.SOLID);
waySegment.drawWideLineSimple(
Legend.COLORS[Legend.COLOR_DEST_LINE],
new IntPoint(pc.getP().getImageCenter().x - imageCollector.xScreenOverscan, pc.getP().getImageCenter().y - imageCollector.yScreenOverscan),
new IntPoint(x, y),
Configuration.getDestLineWidth(), pc
);
}
} catch (Exception e) {
if (imageCollector == null) {
logger.silentexception("No ImageCollector", e);
}
e.printStackTrace();
}
}
/**
*
* Show marker texts, if marker under cursor
*/
public void showMarker(Graphics g) {
}
/**
* Draws the position square, the movement line and the center cross.
*
* @param g Graphics context for drawing
*/
public void showMovement(Graphics g) {
// Avoid exception after route calculation
if ( pc.getP() == null )
return;
centerP = null;
try {
if (imageCollector != null) {
g.setColor(Legend.COLORS[Legend.COLOR_MAP_CURSOR]);
centerP = pc.getP().getImageCenter();
int centerX = centerP.x - imageCollector.xScreenOverscan;
int centerY = centerP.y - imageCollector.yScreenOverscan;
int posX, posY;
//if (!gpsRecenter) {
IntPoint p1 = new IntPoint(0, 0);
pc.getP().forward((pos.latitude * MoreMath.FAC_DECTORAD),
(pos.longitude * MoreMath.FAC_DECTORAD), p1);
posX = p1.getX()-imageCollector.xScreenOverscan;
posY = p1.getY()-imageCollector.yScreenOverscan;
//} else {
//posX = centerX;
//posY = centerY;
//}
g.setColor(Legend.COLORS[Legend.COLOR_MAP_POSINDICATOR]);
float radc = course * MoreMath.FAC_DECTORAD;
int px = posX + (int) (Math.sin(radc) * 20);
int py = posY - (int) (Math.cos(radc) * 20);
// crosshair center cursor
if (!gpsRecenter || gpsRecenterInvalid) {
g.drawLine(centerX, centerY - 12, centerX, centerY + 12);
g.drawLine(centerX - 12, centerY, centerX + 12, centerY);
g.drawArc(centerX - 5, centerY - 5, 10, 10, 0, 360);
}
if (! gpsRecenterInvalid) {
// gps position spot
pc.g.drawImage(gpsRecenterStale ? pc.images.IMG_POS_BG_STALE : pc.images.IMG_POS_BG, posX, posY, CENTERPOS);
// gps position rectangle
g.drawRect(posX - 4, posY - 4, 8, 8);
g.drawLine(posX, posY, px, py);
// draw accuracy circle for position
if (Configuration.getCfgBitState(Configuration.CFGBIT_SHOW_ACCURACY)
&& pc.getP().isOrthogonal()) {
Position pos = getCurrentPosition();
if (pos.accuracy != Float.NaN && pos.accuracy > 0) {
int diaM = (int) pos.accuracy * 2;
int dia = 0;
Node n = new Node();
float scale = pc.getP().getScale();
Projection p = new Proj2D(center,scale, mapWindow.getWidth(), mapWindow.getHeight());
p.inverse(posX, posY, n);
n.radlat += MoreMath.RADIANT_PER_METER * diaM;
IntPoint ip = p.forward(n);
dia = ip.y - posY;
pc.g.drawArc(posX - dia / 2, posY - dia / 2, dia, dia, 0, 360);
}
}
}
}
} catch (Exception e) {
if (imageCollector == null) {
logger.silentexception("No ImageCollector", e);
}
if (centerP == null) {
logger.silentexception("No centerP", e);
}
e.printStackTrace();
}
}
/**
* Show next screen in the sequence of data screens
* (tacho, trip, satellites).
* @param currentScreen Data screen currently shown, use the DATASCREEN_XXX
* constants from this class. Use DATASCREEN_NONE if none of them
* is on screen i.e. the first one should be shown.
*/
public void showNextDataScreen(int currentScreen) {
switch (currentScreen)
{
case DATASCREEN_TACHO:
// Tacho is followed by Trip.
if (guiTrip == null) {
guiTrip = new GuiTrip(this);
}
if (guiTrip != null) {
guiTrip.show();
}
break;
case DATASCREEN_TRIP:
// Trip is followed by Satellites.
if (guiSatellites == null) {
guiSatellites = new GuiSatellites(this, locationProducer);
}
if (guiSatellites != null) {
guiSatellites.show();
}
break;
case DATASCREEN_SATS:
// After Satellites, go back to map.
this.show();
break;
case DATASCREEN_NONE:
default:
// Tacho is first data screen
if (guiTacho == null) {
guiTacho = new GuiTacho(this);
}
if (guiTacho != null) {
guiTacho.show();
}
break;
}
}
public int showMemory(Graphics g, int yc, int la) {
g.setColor(0);
g.drawString(Locale.get("trace.Freemem")/*Freemem: */ + runtime.freeMemory(), 0, yc, Graphics.TOP
| Graphics.LEFT);
yc += la;
g.drawString(Locale.get("trace.Totmem")/*Totmem: */ + runtime.totalMemory(), 0, yc, Graphics.TOP
| Graphics.LEFT);
yc += la;
g.drawString(Locale.get("trace.Percent")/*Percent: */
+ (100f * runtime.freeMemory() / runtime.totalMemory()), 0, yc,
Graphics.TOP | Graphics.LEFT);
yc += la;
g.drawString(Locale.get("trace.ThreadsRunning")/*Threads running: */
+ Thread.activeCount(), 0, yc,
Graphics.TOP | Graphics.LEFT);
yc += la;
g.drawString(Locale.get("trace.Names")/*Names: */ + namesThread.getNameCount(), 0, yc,
Graphics.TOP | Graphics.LEFT);
yc += la;
g.drawString(Locale.get("trace.SingleT")/*Single T: */ + tileReader.getLivingTilesCount() + "/"
+ tileReader.getRequestQueueSize(), 0, yc, Graphics.TOP
| Graphics.LEFT);
yc += la;
g.drawString(Locale.get("trace.FileT")/*File T: */ + dictReader.getLivingTilesCount() + "/"
+ dictReader.getRequestQueueSize() + " Map: " + ImageCollector.icDuration + " ms", 0, yc, Graphics.TOP
| Graphics.LEFT);
yc += la;
g.drawString(Locale.get("trace.LastMsg")/*LastMsg: */ + lastTitleMsg, 0, yc, Graphics.TOP
| Graphics.LEFT);
yc += la;
g.drawString( Locale.get("trace.at")/*at */ + lastTitleMsgClock, 0, yc,
Graphics.TOP | Graphics.LEFT );
return (yc);
}
private void updatePosition() {
if (pc != null) {
pc.center = center.copy();
pc.scale = scale;
pc.course=course;
repaint();
if (locationUpdateListeners != null && !TrackPlayer.isPlaying) {
synchronized (locationUpdateListeners) {
for (int i = 0; i < locationUpdateListeners.size(); i++) {
((LocationUpdateListener)locationUpdateListeners.elementAt(i)).loctionUpdated();
}
}
}
}
}
public synchronized void receivePosition(float lat, float lon, float scale) {
//#debug debug
logger.debug("Now displaying: " + (lat * MoreMath.FAC_RADTODEC) + " | " +
(lon * MoreMath.FAC_RADTODEC));
//We are explicitly setting the map to this position, so we probably don't
//want it to be recentered on the GPS immediately.
gpsRecenter = false;
center.setLatLonRad(lat, lon);
this.scale = scale;
updatePosition();
}
public synchronized void receiveCompassStatus(int status) {
}
public void deviateCompass() {
compassDeviated = (compassDirection + compassDeviation + 360) % 360;
}
public synchronized void receiveCompass(float direction) {
//#debug debug
logger.debug("Got compass reading: " + direction);
compassDirection = (int) direction;
deviateCompass();
// TODO: allow for user to use compass for turning the map in panning mode
// (gpsRenter test below switchable by user setting)
//if (Configuration.getCfgBitState(Configuration.CFGBIT_COMPASS_DIRECTION) && compassProducer != null && gpsRecenter) {
// if (!Configuration.getCfgBitState(Configuration.CFGBIT_COMPASS_AND_MOVEMENT_DIRECTION)) {
// updateCourse(compassDeviated);
// }
//repaint();
//}
// course = compassDeviated;
//}
if (Configuration.getCfgBitState(Configuration.CFGBIT_COMPASS_ALWAYS_ROTATE)
// if user is panning the map, don't rotate by compass
&& gpsRecenter
// if we have autoswitch, rotate by compass only when movement course is not valid
&& !(Configuration.getCfgBitState(Configuration.CFGBIT_COMPASS_AND_MOVEMENT_DIRECTION)
&& (fspeed >= courseMinSpeed && thirdPrevCourse != -1))) {
course = compassDeviated;
updatePosition();
}
}
public static void updateLastUserActionTime() {
lastUserActionTime = System.currentTimeMillis();
}
public static long getDurationSinceLastUserActionTime() {
return System.currentTimeMillis() - lastUserActionTime;
}
public int getCourse() {
return course;
}
public void updateCourse(int newcourse) {
coursegps = newcourse;
/* don't rotate too fast
*/
int courseToSet = course;
if ((newcourse - courseToSet) > 180) {
courseToSet = courseToSet + 360;
}
if ((courseToSet - newcourse) > 180) {
newcourse = newcourse + 360;
}
if (Configuration.getCfgBitState(Configuration.CFGBIT_COMPASS_DIRECTION) && compassProducer != null) {
courseToSet = newcourse;
} else {
// FIXME I think this is too slow a turn at least when course is
// of good quality, turning should be faster. This probably alleviates
// the trouble caused by unreliable gps course. However,
// some kind of heuristic, averaging course or evaluating the
// quality of course and fast or instant rotation with a known good GPS fix
// should be implemented instead of assuming course is always unreliable.
// The course fluctuations are lesser in faster speeds, so if we're constantly
// (for three consecutive locations) above say 5 km/h, a reasonable approach
// could be to use direct course change in that case, and for speed below
// 5 km/h use the slow turning below.
// jkpj 2010-01-17
// on 2011-04-11: jkpj switched from 1/4 rotation back to 3/4 rotation,
// returning to what it was supposed to do before 2010-11-30.
courseToSet = courseToSet + ((newcourse - courseToSet)*3)/4 + 360;
}
course = courseToSet % 360;
validateCourse();
}
public boolean isCourseValid() {
return courseValid;
}
private void invalidateCourse() {
courseValid = false;
}
private void validateCourse() {
courseValid = true;
}
public synchronized void receivePosition(Position pos) {
// FIXME signal on location gained
//#debug info
logger.info("New position: " + pos);
collected++;
if (Configuration.getAutoRecenterToGpsMilliSecs() !=0 &&
getDurationSinceLastUserActionTime() > Configuration.getAutoRecenterToGpsMilliSecs()
&& isShown()
) {
gpsRecenter = true;
//autoZoomed = true;
}
if (Configuration.getLocationProvider() == Configuration.LOCATIONPROVIDER_JSR179) {
if (pos.type == Position.TYPE_GPS_LASTKNOWN) {
// if we have a current cell id fix from cellid location,
// don't overwrite it with a stale GPS location, but ignore the position
// FIXME perhaps compare timestamps here in case the last known gps is later
if (this.pos.type == Position.TYPE_CELLID) {
return;
}
gpsRecenterInvalid = false;
gpsRecenterStale = true;
}
}
this.pos = pos;
if (pos.type == Position.TYPE_GPS || pos.type == Position.TYPE_CELLID || pos.type == Position.TYPE_MANUAL) {
gpsRecenterInvalid = false;
gpsRecenterStale = false;
}
if (gpsRecenter) {
center.setLatLonDeg(pos.latitude, pos.longitude);
gpsNode.setLatLonDeg(pos.latitude, pos.longitude);
if (Configuration.getCfgBitState(Configuration.CFGBIT_KEEP_ON_ROAD_IN_ROUTE_GUIDANCE) && RouteLineProducer.isRouteLineProduced()) {
center = routePointCenter.copy();
}
speed = (int) (pos.speed * 3.6f);
fspeed = pos.speed * 3.6f;
if (Configuration.getCfgBitState(Configuration.CFGBIT_COMPASS_DIRECTION) && compassProducer != null) {
deviateCompass();
}
// auto-fallback mode where course is from GPS at high speeds and from compass
// at low speeds
if (Configuration.getCfgBitState(Configuration.CFGBIT_COMPASS_DIRECTION) && compassProducer != null && !Configuration.getCfgBitState(Configuration.CFGBIT_COMPASS_AND_MOVEMENT_DIRECTION)) {
updateCourse(compassDeviated);
} else if (fspeed >= courseMinSpeed && pos.course != Float.NaN ) {
// Problem: resolve issue erratic course due to GPS fluctuation
// when GPS reception is poor (maybe 3..7S),
// causes unnecessary and disturbing map rotation when device is in one location
// Heuristic for solving: After being still, require
// three consecutive over-the-limit speed readings with roughly the
// same course
if (thirdPrevCourse != -1) {
// first check for normal flow of things, we've had three valid courses after movement start
updateCourse((int) pos.course);
thirdPrevCourse = secondPrevCourse;
secondPrevCourse = prevCourse;
// check for compass deviation auto-update, do it if set
if (Configuration.getCfgBitState(Configuration.CFGBIT_COMPASS_AUTOCALIBRATE)) {
compassDeviation = (int) pos.course - compassDirection;
deviateCompass();
}
} else if (prevCourse == -1) {
if (Configuration.getCfgBitState(Configuration.CFGBIT_COMPASS_AND_MOVEMENT_DIRECTION)) {
updateCourse(compassDeviated);
} // previous course was invalid,
// don't set course yet, but set the first tentatively good course
prevCourse = (int) pos.course;
} else if (secondPrevCourse == -1) {
// the previous course was the first good one.
// If this course is in the same 60-degree
// sector as the first course, we have two valid courses
if (Math.abs(prevCourse - (int)pos.course) < 30 || Math.abs(prevCourse - (int)pos.course) > 330) {
secondPrevCourse = prevCourse;
}
if (Configuration.getCfgBitState(Configuration.CFGBIT_COMPASS_AND_MOVEMENT_DIRECTION)) {
updateCourse(compassDeviated);
}
} else {
// we have received two previous valid curses, check for this one
if (Math.abs(prevCourse - (int)pos.course) < 30 || Math.abs(prevCourse - (int)pos.course) > 330) {
thirdPrevCourse = secondPrevCourse;
secondPrevCourse = prevCourse;
updateCourse((int) pos.course);
} else {
secondPrevCourse = -1;
if (Configuration.getCfgBitState(Configuration.CFGBIT_COMPASS_AND_MOVEMENT_DIRECTION)) {
updateCourse(compassDeviated);
}
}
}
prevCourse = (int) pos.course;
} else {
// speed under the minimum. If it went under the limit now, do a heuristic
// to decide a proper course.
// invalidate all prev courses
if (thirdPrevCourse != -1) {
// speed just went under the threshold
// - if the last readings are not within the 30 degree sector of
// the previous one, restore course to the third previous course,
// it's probable that it's more reliable than the last
// course, as we see that at traffic light stops
// an incorrect course is shown relatively often
if ((Math.abs(prevCourse - secondPrevCourse) < 15 || Math.abs(prevCourse - secondPrevCourse) > 345)
&& (Math.abs(thirdPrevCourse - secondPrevCourse) < 15 || Math.abs(thirdPrevCourse - secondPrevCourse) > 345)) {
// we're OK
} else {
updateCourse(thirdPrevCourse);
}
}
prevCourse = -1;
secondPrevCourse = -1;
thirdPrevCourse = -1;
if (Configuration.getCfgBitState(Configuration.CFGBIT_COMPASS_AND_MOVEMENT_DIRECTION)) {
updateCourse(compassDeviated);
}
}
}
pos.altitude += Configuration.getAltitudeCorrection();
altitude = (int) (pos.altitude);
if (gpx.isRecordingTrk()) {
try {
// don't tracklog manual cellid position or gps start/stop last known position
if ((Configuration.getLocationProvider() == Configuration.LOCATIONPROVIDER_JSR179
&& pos.type == Position.TYPE_CELLID) || pos.type == Position.TYPE_GPS_LASTKNOWN) {
} else {
gpx.addTrkPt(pos);
}
} catch (Exception e) {
receiveMessage(e.getMessage());
}
}
if (Configuration.getCfgBitState(Configuration.CFGBIT_AUTOZOOM)
&& gpsRecenter
&& (isGpsConnected() || TrackPlayer.isPlaying)
&& autoZoomed
&& pc.getP() != null
&& pos.speed != Float.NaN // if speed is unknown do not autozoom
&& pos.speed != 0 // if speed is 0 do not autozoom
) {
// the minimumScale at 20km/h and below is equivalent to having zoomed in manually once from the startup zoom level
final float minimumScale = Configuration.getRealBaseScale() / Configuration.getZoomFactor();
final int minimumSpeed = 20;
// the maximumScale at 160km/h and above is equivalent to having zoomed out manually once from the startup zoom level
final float maximumScale = Configuration.getRealBaseScale() * Configuration.getZoomFactor();
final int maximumSpeed = 160;
int speedForScale = speed;
float newScale = minimumScale + (maximumScale - minimumScale) * (speedForScale - minimumSpeed) / (maximumSpeed - minimumSpeed);
// make sure the new scale is within the minimum / maximum scale values
if (newScale < minimumScale) {
newScale = minimumScale;
} else if (newScale > maximumScale) {
newScale = maximumScale;
}
// autozoom in more at the last 200 m of the route
if (route != null && RouteInstructions.getDstRouteToDestination() <= 200) {
float newScale2 = newScale;
newScale2 = newScale / (1f + (200f - RouteInstructions.getDstRouteToDestination())/ 200f);
// fixed increased zoom for the last 100 m
newScale = Math.max(newScale2, newScale / Configuration.getZoomFactor());
}
scale = newScale;
// // calculate meters to top of screen
// float topLat = pc.getP().getMaxLat();
// float topLon = (pc.getP().getMinLon() + pc.getP().getMaxLon()) / 2f;
// float distance = MoreMath.dist(center.radlat, center.radlon, topLat, topLon );
// System.out.println("current distance to top of screen: " + distance);
//
// // avoid zooming in or out too far
// int speedForScale = course;
// if (speedForScale < 30) {
// speedForScale = 30;
// } else if (speedForScale > 160) {
// speedForScale = 160;
// }
//
// final float SECONDS_TO_PREVIEW = 45f;
// float metersToPreview = (float) speedForScale / 3.6f * SECONDS_TO_PREVIEW;
// System.out.println(metersToPreview + "meters to preview at " + speedForScale + "km/h");
//
// if (metersToPreview < 2000) {
// // calculate top position that needs to be visible to preview the metersToPreview
// topLat = center.radlat + (topLat - center.radlat) * metersToPreview / distance;
// topLon = center.radlon + (topLon - center.radlon) * metersToPreview / distance;
// System.out.println("new distance to top:" + MoreMath.dist(center.radlat, center.radlon, topLat, topLon ));
//
// /*
// * calculate scale factor, we multiply this again with 2 * 1.2 = 2.4 to take into account we calculated half the screen height
// * and 1.2f is probably the factor the PaintContext is larger than the display size
// * (new scale is calculated similiar to GuiWaypoint)
// */
// IntPoint intPoint1 = new IntPoint(0, 0);
// IntPoint intPoint2 = new IntPoint(getWidth(), getHeight());
// Node n1 = new Node(center.radlat, center.radlon, true);
// Node n2 = new Node(topLat, topLon, true);
// scale = pc.getP().getScale(n1, n2, intPoint1, intPoint2) * 2.4f;
// }
}
updatePosition();
}
public synchronized Position getCurrentPosition() {
return this.pos;
}
public synchronized void receiveMessage(String s) {
// #debug info
logger.info("Setting title: " + s);
currentTitleMsg = s;
synchronized (this) {
/*
* only increase the number of current title messages
* if the timer already has been set in the display code
*/
if (setTitleMsgTimeout == 0) {
currentTitleMsgOpenCount++;
}
setTitleMsgTimeout = 3000;
}
lastTitleMsgClock = DateTimeTools.getClock(System.currentTimeMillis(), false);
repaint();
}
public void receiveSatellites(Satellite[] sats) {
// Not interested
}
/** Shows an alert message
*
* @param title The title of the alert
* @param message The message text
* @param timeout Timeout in ms. Please reserve enough time so the user can
* actually read the message. Use Alert.FOREVER if you want no timeout.
*/
public synchronized void alert(String title, String message, int timeout) {
// #debug info
logger.info("Showing trace alert: " + title + ": " + message);
if (timeout == Alert.FOREVER) {
timeout = 10000;
}
currentAlertTitle = title;
currentAlertMessage = message;
synchronized (this) {
/*
* only increase the number of current open alerts
* if the timer already has been set in the display code
*/
if (setAlertTimeout == 0) {
currentAlertsOpenCount++;
}
setAlertTimeout = timeout;
}
repaint();
}
public MIDlet getParent() {
return parent;
}
public void pointerPressed(int x, int y) {
//#if polish.android
previousBackPress = false;
//#endif
pointerDown = true;
if (coordsForOthers(x, y)) {
// for icon menu
if (isShowingSplitTraceIconMenu() && traceIconMenu != null) {
traceIconMenu.pointerPressed(x, y);
return;
}
if (isShowingSplitSearch() && guiSearch != null) {
guiSearch.pointerPressed(x, y);
return;
}
if (isShowingSplitSetup() && guiDiscoverIconMenu != null) {
guiDiscoverIconMenu.pointerPressed(x, y);
return;
}
if (isShowingSplitCMS() && cmsl != null) {
cmsl.pointerPressed(x, y);
return;
}
}
updateLastUserActionTime();
long currTime = System.currentTimeMillis();
pointerDragged = false;
pointerDraggedMuch = false;
pointerActionDone = false;
// remember center when the pointer was pressed for dragging
centerPointerPressedN = center.copy();
if (imageCollector != null) {
panProjection=imageCollector.getCurrentProjection();
pickPointStart=panProjection.inverse(x,y, pickPointStart);
} else {
panProjection = null;
}
// remember the LayoutElement the pointer is pressed down at, this will also highlight it on the display
int touchedElementId = tl.getElementIdAtPointer(x, y);
if (touchedElementId >= 0 && (!keyboardLocked || tl.getActionIdAtPointer(x, y) == Trace.ICON_MENU)
&&
tl.isAnyActionIdAtPointer(x, y)
) {
tl.setTouchedElement((LayoutElement) tl.elementAt(touchedElementId));
repaint();
}
// check for double press
if (!keyboardLocked && currTime - pressedPointerTime < DOUBLETAP_MAXDELAY) {
doubleTap(x, y);
return;
}
// Remember the time and position the pointer was pressed after the check for double tap,
// so the double tap code will check for the position of the first of the two taps
pressedPointerTime = currTime;
Trace.touchX = x;
Trace.touchY = y;
// Give a message if keyboard/user interface is locked.
// This must be done after remembering the touchX/Y positions as they are needed to unlock
if (keyboardLocked) {
keyPressed(0);
return;
}
// // when these statements are reached, no double tap action has been executed,
// // so check here if there's currently already a TimerTask waiting for a single tap.
// // If yes, perform the current single tap action immediately before starting the next TimerTask
// if (checkingForSingleTap && !pointerDraggedMuch) {
// singleTap();
// pointerActionDone = false;
// }
//
longTapTimerTask = new TimerTask() {
public void run() {
// if no action (e.g. from double tap) is already done
// and the pointer did not move or if it was pressed on a control and not moved much
if (!pointerActionDone) {
if (System.currentTimeMillis() - pressedPointerTime >= LONGTAP_DELAY){
longTap(false);
}
}
}
};
try {
// set timer to continue check if this is a long tap
ShareNav.getTimer().schedule(longTapTimerTask, LONGTAP_DELAY);
} catch (Exception e) {
logger.error(Locale.get("trace.NoLongTapTimerTask")/*No LongTap TimerTask: */ + e.toString());
}
}
public void pointerReleased(int x, int y) {
pointerDown = false;
mapBrowsing = false;
if (coordsForOthers(x, y)) {
// for icon menu
if (isShowingSplitTraceIconMenu() && traceIconMenu != null) {
traceIconMenu.pointerReleased(x, y);
return;
}
if (isShowingSplitSearch() && guiSearch != null) {
guiSearch.pointerReleased(x, y);
return;
}
if (isShowingSplitSetup() && guiDiscoverIconMenu != null) {
guiDiscoverIconMenu.pointerReleased(x, y);
return;
}
if (isShowingSplitCMS() && cmsl != null) {
cmsl.pointerReleased(x, y);
return;
}
}
// releasing the pointer cancels the check for long tap
if (longTapTimerTask != null) {
longTapTimerTask.cancel();
}
// releasing the pointer will clear the highlighting of the touched element
if (tl.getTouchedElement() != null) {
tl.clearTouchedElement();
repaint();
}
if (pointerDraggedMuch &&
Math.abs(x - Trace.touchX) <= DRAGGEDMUCH_THRESHOLD
&&
Math.abs(y - Trace.touchY) <= DRAGGEDMUCH_THRESHOLD
) {
// reset draggedmuch if we come back to the original
// touch place (e.g. finger was moved momentarily away and
// back due to a bump on the road).
pointerDraggedMuch = false;
}
if (!pointerActionDone && !keyboardLocked) {
touchReleaseX = x;
touchReleaseY = y;
if (Configuration.getCfgBitState(Configuration.CFGBIT_MAPTAP_SINGLE)
&& (!Configuration.getCfgBitState(Configuration.CFGBIT_CLICKABLE_MAPOBJECTS) || getClickableMarker(x, y) == null)
&& !tl.isAnyActionIdAtPointer(x, y)
&& !pointerDraggedMuch) {
highlightOnScreenButtons();
}
startDoubleTapTimer(x, y);
//#if not polish.android
if (pointerDragged) {
pointerDragged(x , y);
return;
}
//#endif
}
}
private void startDoubleTapTimer(int x, int y) {
// check for a single tap in a timer started after the maximum double tap delay
// if the timer will not be cancelled by a double tap, the timer will execute the single tap command
//#if polish.android
if (doubleTapActive(x, y)) {
singleTapTimerTask = new TimerTask() {
public void run() {
if (!keyboardLocked) {
singleTap(touchReleaseX, touchReleaseY);
}
}
};
}
//#else
singleTapTimerTask = new TimerTask() {
public void run() {
if (!keyboardLocked) {
singleTap(touchReleaseX, touchReleaseY);
}
}
};
//#endif
try {
// set timer to check if this is a single tap
// FIXME activate these tests also for J2ME after testing
//#if polish.android
if (doubleTapActive(x, y)) {
ShareNav.getTimer().schedule(singleTapTimerTask, DOUBLETAP_MAXDELAY);
} else {
singleTap(touchReleaseX, touchReleaseY);
}
//#else
ShareNav.getTimer().schedule(singleTapTimerTask, DOUBLETAP_MAXDELAY);
//#endif
} catch (Exception e) {
logger.error(Locale.get("trace.NoSingleTapTimerTask")/*No SingleTap TimerTask: */ + e.toString());
}
}
//#if polish.android
public void mtPointerDragged (float newscale) {
scale = newscale;
autoZoomed = false;
updateLastUserActionTime();
repaint();
return;
}
public void mtPointerRotated (float newangle) {
course = (int) newangle;
updateLastUserActionTime();
updatePosition();
repaint();
return;
}
//#endif
public int angleDiff(int a, int b) {
int diff = a - b;
if (diff < 0) {
diff += 360;
}
if (diff > 180) {
diff = 360 - diff;
}
return diff;
}
public boolean doubleTapActive(int x, int y) {
return (Configuration.getCfgBitState(Configuration.CFGBIT_MAPTAP_DOUBLE)
&& !tl.isAnyActionIdAtPointer(x, y))
|| tl.pointerHasDoubleTapAction(x, y);
}
public void pointerDragged (int x, int y) {
if (coordsForOthers(x, y)) {
// for icon menu
if (isShowingSplitTraceIconMenu() && traceIconMenu != null) {
traceIconMenu.pointerDragged(x, y);
return;
}
if (isShowingSplitSearch() && guiSearch != null) {
guiSearch.pointerDragged(x, y);
return;
}
if (isShowingSplitSetup() && guiDiscoverIconMenu != null) {
guiDiscoverIconMenu.pointerDragged(x, y);
return;
}
if (isShowingSplitCMS() && cmsl != null) {
cmsl.pointerDragged(x, y);
return;
}
}
// check if there's been much movement, do this before the slide lock/unlock
// to avoid a single tap action when not sliding enough
if (Math.abs(x - Trace.touchX) > DRAGGEDMUCH_THRESHOLD
||
Math.abs(y - Trace.touchY) > DRAGGEDMUCH_THRESHOLD
) {
pointerDraggedMuch = true;
// avoid double tap triggering on fast consecutive drag actions starting at almost the same position
pressedPointerTime = 0;
// zero long press timer which will fire otherwise with setting
// pressedPointerTime to 0; we don't want a pointer dragged much
// to be interpreted as a long tap
if (longTapTimerTask != null) {
longTapTimerTask.cancel();
}
}
if (pointerDown && pointerDraggedMuch) {
mapBrowsing = true;
}
updateLastUserActionTime();
LayoutElement e = tl.getElementAtPointer(x, y);
if (tl.getTouchedElement() != e) {
// leaving the touched element cancels the check for long tap
if (longTapTimerTask != null) {
longTapTimerTask.cancel();
}
tl.clearTouchedElement();
repaint();
}
// If the initially touched element is reached again during dragging, highlight it
if (tl.getElementAtPointer(touchX, touchY) == e && tl.isAnyActionIdAtPointer(x, y)) {
tl.setTouchedElement(e);
repaint();
}
if (pointerActionDone) {
return;
}
// slide at least 1/4 display width to lock / unlock ShareNav
if (tl.getActionIdAtPointer(touchX, touchY) == Trace.ICON_MENU) {
if ( tl.getActionIdAtPointer(x, y) == Trace.ICON_MENU
&&
x - touchX > getWidth() / 4
) {
commandAction(TOGGLE_KEY_LOCK_CMD);
pointerActionDone = true;
}
return;
}
if (keyboardLocked) {
return;
}
pointerDragged = true;
// do not start map dragging on a touch control if only dragged slightly
// on Android, not even when not on a touch control, so
// pinch zoom won't always turn off follow GPS mode
if (!pointerDraggedMuch
//#if polish.android
//#else
&& tl.getElementIdAtPointer(touchX, touchY) >= 0
//#endif
) {
return;
}
if (tl.getElementIdAtPointer(touchX, touchY) < 0 && imageCollector != null && panProjection != null) {
// difference between where the pointer was pressed and is currently dragged
// int diffX = Trace.touchX - x;
// int diffY = Trace.touchY - y;
//
// IntPoint centerPointerPressedP = new IntPoint();
pickPointEnd=panProjection.inverse(x,y, pickPointEnd);
center.radlat=centerPointerPressedN.radlat-(pickPointEnd.radlat-pickPointStart.radlat);
center.radlon=centerPointerPressedN.radlon-(pickPointEnd.radlon-pickPointStart.radlon);
// System.out.println("diff " + diffX + "/" + diffY + " " + (pickPointEnd.radlat-pickPointStart.radlat) + "/" + (pickPointEnd.radlon-pickPointStart.radlon) );
imageCollector.newDataReady();
gpsRecenter = false;
long lCurrentTime = System.currentTimeMillis();
if ( lCurrentTime - lLastDragTime > 333) {
lLastDragTime = lCurrentTime;
repaint();
}
}
}
private void singleTap(int x, int y) {
pointerActionDone = true;
// if not tapping a control, then the map area must be tapped so we set the touchable button sizes
if (tl.getElementIdAtPointer(touchX, touchY) < 0) {
if (Configuration.getCfgBitState(Configuration.CFGBIT_MAPTAP_SINGLE)) {
// if pointer was dragged much, do not recognize a single tap on the map
if (pointerDraggedMuch) {
return;
}
// #debug debug
logger.debug("single tap map");
// check for clickable markers
// System.out.println("Checking for clickable markers");
boolean markerClicked = false;
ClickableCoords coords = getClickableMarker(x, y);
if (coords != null) {
//if (internetAccessAllowed()) {
markerClicked = true;
// TODO maybe later add an
// option or options to have
// marker click directly either
// * open the URL
// * edit node in OSM
// * save a waypoint
// * log the place to a webservice
// * something else
// if (coords.url != null) {
// GuiWebInfo.openUrl(coords.url);
// return;
//} else {
longTap(true);
//}
//} else {
//String text = "";
//if (coords.url != null) {
//text += coords.url;
//}
//if (coords.phone != null) {
//if (text.length() != 0) {
//text += " / ";
//}
//text += coords.phone;
//}
//alert("Contact", text, 5000);
//}
}
if (!markerClicked && !tl.bigOnScreenButtons) {
highlightOnScreenButtons();
}
}
repaint();
} else if (tl.getElementIdAtPointer(x, y) == tl.getElementIdAtPointer(touchX, touchY)) {
tl.clearTouchedElement();
int actionId = tl.getActionIdAtPointer(x, y);
if (actionId > 0) {
// #debug debug
logger.debug("single tap button: " + actionId + " x: " + touchX + " y: " + touchY);
if (System.currentTimeMillis() < (lastBackLightOnTime + 5000)) {
if (actionId == ZOOM_IN_CMD) {
actionId = PAN_RIGHT2_CMD;
} else if (actionId == ZOOM_OUT_CMD) {
actionId = PAN_LEFT2_CMD;
}
} else if (manualRotationMode) {
if (actionId == ZOOM_IN_CMD) {
actionId = PAN_LEFT2_CMD;
} else if (actionId == ZOOM_OUT_CMD) {
actionId = PAN_RIGHT2_CMD;
}
} else if (TrackPlayer.isPlaying) {
if (actionId == ZOOM_IN_CMD) {
actionId = PAN_RIGHT2_CMD;
} else if (actionId == ZOOM_OUT_CMD) {
actionId = PAN_LEFT2_CMD;
}
}
commandAction(actionId);
repaint();
}
}
}
private void highlightOnScreenButtons() {
tl.setOnScreenButtonSize(true);
// to enable clickable markers only when single-tapped
//if (Configuration.getCfgBitState(Configuration.CFGBIT_CLICKABLE_MAPOBJECTS)) {
// newDataReady();
//}
// set timer to continuously check if the last user interaction is old enough to make the buttons small again
final long BIGBUTTON_DURATION = 5000;
bigButtonTimerTask = new TimerTask() {
public void run() {
if (System.currentTimeMillis() - lastUserActionTime > BIGBUTTON_DURATION ) {
// make the on screen touch buttons small again
tl.setOnScreenButtonSize(false);
requestRedraw();
// to enable clickable markers only when single-tapped
//if (Configuration.getCfgBitState(Configuration.CFGBIT_CLICKABLE_MAPOBJECTS)) {
// newDataReady();
//}
bigButtonTimerTask.cancel();
}
}
};
try {
ShareNav.getTimer().schedule(bigButtonTimerTask, BIGBUTTON_DURATION, 500);
} catch (Exception e) {
logger.error("Error scheduling bigButtonTimerTask: " + e.toString());
}
}
private void doubleTap(int x, int y) {
// if not double tapping a control, then the map area must be double tapped and we zoom in
if (tl.getElementIdAtPointer(touchX, touchY) < 0) {
if (doubleTapActive(touchX, touchY)) {
// if this is a double press on the map, cancel the timer checking for a single press
if (singleTapTimerTask != null) {
singleTapTimerTask.cancel();
}
// if pointer was dragged much, do not recognize a double tap on the map
if (pointerDraggedMuch) {
return;
}
//#debug debug
logger.debug("double tap map");
pointerActionDone = true;
commandAction(ZOOM_IN_CMD);
}
repaint();
return;
} else if (tl.getTouchedElement() == tl.getElementAtPointer(x, y) ){
// double tapping a control
int actionId = tl.getActionIdDoubleAtPointer(x, y);
//#debug debug
logger.debug("double tap button: " + actionId + " x: " + x + " y: " + x);
if (actionId > 0) {
// if this is a double press on a control, cancel the timer checking for a single press
if (singleTapTimerTask != null) {
singleTapTimerTask.cancel();
}
pointerActionDone = true;
commandAction(actionId);
tl.clearTouchedElement();
repaint();
return;
} else {
singleTap(x, y);
}
}
}
private ClickableCoords getClickableMarker(int x, int y) {
System.out.println("Click coords: " + x + " " + y);
if (clickableMarkers != null) {
for (int i = 0; i < clickableMarkers.size(); i++) {
ClickableCoords coords = (ClickableCoords)clickableMarkers.elementAt(i);
// System.out.println("Marker coords: " + coords.x + " " + coords.y);
if (Math.abs(coords.x - x) <= Configuration.getTouchMarkerDiameter() / 2
&& Math.abs(coords.y - y) <= Configuration.getTouchMarkerDiameter() / 2) {
return coords;
}
}
}
return null;
}
private void longTap(boolean force) {
// if not tapping a control, then the map area must be tapped so we do the long tap action for the map area
if (tl.getElementIdAtPointer(touchX, touchY) < 0 && panProjection != null) {
if (!pointerDraggedMuch && (Configuration.getCfgBitState(Configuration.CFGBIT_MAPTAP_LONG) || force || getClickableMarker(touchX, touchY) != null)) {
pointerActionDone = true;
//#debug debug
logger.debug("long tap map");
// if we clicked a clickable marker, get coords from the marker instead of tap
ClickableCoords coords = getClickableMarker(touchX, touchY);
String url = null;
String phone = null;
if (coords != null) {
touchX = coords.x;
touchY = coords.y;
url = getUrl(coords.urlIdx);
phone = getUrl(coords.phoneIdx);
}
// long tap map to open a place-related menu
// use the place of touch instead of old center as position,
pickPointEnd=panProjection.inverse(touchX + imageCollector.xScreenOverscan,
touchY + imageCollector.yScreenOverscan, pickPointEnd);
Position oPos = new Position(pickPointEnd.radlat, pickPointEnd.radlon,
0.0f, 0.0f, 0.0f, 0, 0);
GuiWebInfo gWeb = new GuiWebInfo(this, oPos, pc, true, coords != null ? url : null,
coords != null ? phone : null,
coords != null ? coords.nodeID : -1);
gWeb.show();
}
return;
// long tapping a control
} else {
int actionId = tl.getActionIdLongAtPointer(touchX, touchY);
if (actionId > 0 && tl.getElementAtPointer(touchX, touchY) == tl.getTouchedElement()) {
tl.clearTouchedElement();
repaint();
pointerActionDone = true;
//#debug debug
logger.debug("long tap button: " + actionId + " x: " + touchX + " y: " + touchY);
commandAction(actionId);
}
}
}
/**
* Returns the command used to go to the data screens.
* Needed by the data screens so they can find the key codes used for this
* as they have to use them too.
* @return Command
*/
public Command getDataScreenCommand() {
return CMDS[DATASCREEN_CMD];
}
public Tile getDict(byte zl) {
return tiles[zl];
}
public void setDict(Tile dict, byte zl) {
tiles[zl] = dict;
// Tile.trace=this;
//addCommand(REFRESH_CMD);
// if (zl == 3) {
// setTitle(null);
// } else {
// setTitle("dict " + zl + "ready");
// }
if (zl == 0) {
// read saved position from Configuration
Configuration.getStartupPos(center);
if (center.radlat == 0.0f && center.radlon == 0.0f) {
// if no saved position use center of map
dict.getCenter(center);
}
// read saved destination position from Configuration
if (Configuration.getCfgBitState(Configuration.CFGBIT_SAVED_DESTPOS_VALID)) {
Node destNode = new Node();
Configuration.getDestPos(destNode);
setDestination(new RoutePositionMark(destNode.radlat, destNode.radlon));
}
if (pc != null) {
pc.center = center.copy();
pc.scale = scale;
pc.course = course;
}
}
updatePosition();
}
public void setBaseTilesRead(boolean read) {
baseTilesRead = read;
}
public void receiveStatistics(int[] statRecord, byte quality) {
this.btquality = quality;
this.statRecord = statRecord;
repaint();
}
public synchronized void locationDecoderEnd() {
//#debug info
logger.info("enter locationDecoderEnd");
if (Configuration.getCfgBitState(Configuration.CFGBIT_SND_DISCONNECT)) {
// some fault tolerance - will crash without a map
if (Legend.isValid) {
ShareNav.mNoiseMaker.playSound("DISCONNECT");
}
}
if (Configuration.getCfgBitState(Configuration.CFGBIT_GPS_TIME)) {
pos.gpsTimeMillis = 0;
}
//if (gpx != null) {
// /**
// * Close and Save the gpx recording, to ensure we don't loose data
// */
// gpx.saveTrk(false);
//}
removeCommand(CMDS[DISCONNECT_GPS_CMD]);
if (locationProducer == null) {
//#debug info
logger.info("leave locationDecoderEnd no producer");
return;
}
locationProducer = null;
running = false;
notify();
addCommand(CMDS[CONNECT_GPS_CMD]);
// addCommand(START_RECORD_CMD);
//#debug info
logger.info("end locationDecoderEnd");
}
public void receiveStatus(byte status, int satsReceived) {
if (status != LocationMsgReceiver.STATUS_ON
&& status != LocationMsgReceiver.STATUS_2D
&& status != LocationMsgReceiver.STATUS_3D
&& status != LocationMsgReceiver.STATUS_DGPS) {
// no fix, invalidate speed heuristic and GPS time
prevCourse = -1;
secondPrevCourse = -1;
thirdPrevCourse = -1;
if (Configuration.getCfgBitState(Configuration.CFGBIT_GPS_TIME)) {
pos.gpsTimeMillis = 0;
}
}
// FIXME signal a sound on location gained or lost
solution = status;
solutionStr = LocationMsgReceiverList.getCurrentStatusString(status, satsReceived);
repaint();
// to update e.g. tacho
if (locationUpdateListeners != null && !TrackPlayer.isPlaying) {
synchronized (locationUpdateListeners) {
for (int i = 0; i < locationUpdateListeners.size(); i++) {
((LocationUpdateListener)locationUpdateListeners.elementAt(i)).loctionUpdated();
}
}
}
}
public String getName(int idx) {
if (idx < 0) {
return null;
}
return namesThread.getName(idx);
}
public String getUrl(int idx) {
if (idx < 0) {
return null;
}
return urlsThread.getUrl(idx);
}
public Vector fulltextSearch (String snippet, CancelMonitorInterface cmi) {
return namesThread.fulltextSearch(snippet, cmi);
}
// this is called by ImageCollector
public void requestRedraw() {
repaint();
}
public void newDataReady() {
if (imageCollector != null) {
imageCollector.newDataReady();
}
}
//#if polish.android
public void activateAndroidListeners() {
View androidView = (View) CanvasBridge.current();
androidView.setOnKeyListener(new OnKeyListener()
{
public boolean onKey(View v, int keyCode, KeyEvent event)
{
if (event.getAction() == KeyEvent.ACTION_DOWN
|| event.getAction() == KeyEvent.ACTION_UP)
{
//eat menu code in the map screen so the system won't handle it
if (keyCode == KeyEvent.KEYCODE_MENU && imageCollector != null && imageCollector.isRunning())
{
if (event.getAction() == KeyEvent.ACTION_UP) {
if (keyboardLocked) {
// show alert in keypressed() that keyboard is locked
keyPressed(0);
} else {
commandAction(Trace.ICON_MENU);
}
}
// but if icon menu is not enabled, return false so
// J2MEPolish will handle this and user gets a text
// menu
return Configuration.getCfgBitState(Configuration.CFGBIT_ICONMENUS);
}
}
if (keyCode == KeyEvent.KEYCODE_SEARCH) {
// #debug debug
logger.debug(" Turning key into SEARCH_CMD");
commandAction(Trace.getInstance().getCommand(Trace.SEARCH_CMD), (Displayable) null);
return true;
}
return false;
}
});
androidView.setFocusable(true);
androidView.setFocusableInTouchMode(true);
androidView.requestFocus();
androidView.setOnTouchListener(this);
}
//#endif
public void show() {
//Display.getDisplay(parent).setCurrent(this);
Legend.freeDrawnWayAndAreaSearchImages();
ShareNav.getInstance().show(this);
setFullScreenMode(Configuration.getCfgBitState(Configuration.CFGBIT_FULLSCREEN));
updateLastUserActionTime();
repaint();
//#if polish.android
activateAndroidListeners();
//#endif
}
public void recreateTraceLayout() {
// don't re-half screen size in split-screen mode
// refreshWindowLayout((mapMaxX - mapMinX), (mapMaxY - mapMinY));
tl = new TraceLayout(mapWindow);
}
public boolean mapLayoutIsPortrait() {
return mapWindow.isPortrait();
}
public boolean deviceLayoutIsPortrait() {
return rootWindow.isPortrait();
}
public void resetSize() {
sizeChanged(getWidth(), getHeight());
}
public void locationDecoderEnd(String msg) {
receiveMessage(msg);
locationDecoderEnd();
}
public PositionMark getDestination() {
return dest;
}
public PositionMark getPosMark() {
PositionMark posMark = null;
if (gpsRecenter) {
// TODO: If we lose the fix the old position and height
// will be used silently -> we should inform the user
// here that we have no fix - he may not know what's going on.
posMark = new PositionMark(center.radlat, center.radlon,
(int)pos.altitude, pos.timeMillis,
/* fix */ (byte)-1, /* sats */ (byte)-1,
/* sym */ (byte)-1, /* type */ (byte)-1);
} else {
// Cursor does not point to current position
// -> it does not make sense to add elevation and GPS fix info.
posMark = new PositionMark(center.radlat, center.radlon,
PositionMark.INVALID_ELEVATION,
pos.timeMillis, /* fix */ (byte)-1,
/* sats */ (byte)-1, /* sym */ (byte)-1,
/* type */ (byte)-1);
}
return posMark;
}
public void setDestination(RoutePositionMark dest) {
Routing.dropToConnectionsCache();
movedAwayFromDest = false;
endRouting();
this.dest = dest;
pc.dest = dest;
if (dest != null) {
//#debug info
logger.info("Setting destination to " + dest.toString());
// move map only to the destination, if GUI is not optimized for routing
//if (! Configuration.getCfgBitState(Configuration.CFGBIT_ICONMENUS_ROUTING_OPTIMIZED)) {
// commandAction(SHOW_DEST_CMD);
//}
if (Configuration.getCfgBitState(Configuration.CFGBIT_AUTOSAVE_DESTPOS)) {
Configuration.setDestPos(new Node(dest.lat, dest.lon, true));
Configuration.setCfgBitSavedState(Configuration.CFGBIT_SAVED_DESTPOS_VALID, true);
}
} else {
Configuration.setCfgBitSavedState(Configuration.CFGBIT_SAVED_DESTPOS_VALID, false);
//#debug info
logger.info("Setting destination to null");
}
}
public void endRouting() {
RouteInstructions.initialRecalcDone = false;
RouteInstructions.icCountOffRouteDetected = 0;
RouteInstructions.routeInstructionsHeight = 0;
RouteInstructions.abortRouteLineProduction();
setRoute(null);
RouteConnectionTraces.clear();
}
/**
* This is the callback routine if RouteCalculation is ready
* @param route
*/
public void setRoute(Vector route) {
synchronized(this) {
this.route = route;
}
if (this.route != null) {
// reset off-route as soon as first route connection is known
RouteInstructions.resetOffRoute(this.route, center);
if (ri == null) {
ri = new RouteInstructions(this);
}
// show map during route line production
if (Configuration.getContinueMapWhileRouteing() == Configuration.continueMap_At_Route_Line_Creation) {
resumeImageCollectorAfterRouteCalc();
}
ri.newRoute(this.route);
if (routeEngine != null) {
if (routeEngine.routeStartsAgainstMovingDirection) {
ri.forceAgainstDirection();
}
} else {
alert("Warning", "routeEngine==null", 3000);
}
oldRecalculationTime = System.currentTimeMillis();
}
// show map always after route calculation
resumeImageCollectorAfterRouteCalc();
routeCalc=false;
routeEngine=null;
}
private void resumeImageCollectorAfterRouteCalc() {
try {
if (imageCollector == null) {
startImageCollector();
// imageCollector thread starts up suspended,
// so we need to resume it
imageCollector.resume();
} else if (imageCollector != null) {
imageCollector.newDataReady();
}
repaint();
} catch (Exception e) {
logger.exception(Locale.get("trace.InTraceResumeImageCollector")/*In Trace.resumeImageCollector*/, e);
}
}
/**
* If we are running out of memory, try
* dropping all the caches in order to try
* and recover from out of memory errors.
* Not guaranteed to work, as it needs
* to allocate some memory for dropping
* caches.
*/
public void dropCache() {
tileReader.dropCache();
dictReader.dropCache();
System.gc();
namesThread.dropCache();
urlsThread.dropCache();
System.gc();
if (gpx != null) {
gpx.dropCache();
}
}
public QueueDataReader getDataReader() {
return tileReader;
}
public QueueDictReader getDictReader() {
return dictReader;
}
protected void hideNotify() {
//#debug debug
logger.debug("Hide notify has been called, screen will no longer be updated");
if (imageCollector != null) {
imageCollector.suspend();
}
}
protected void showNotify() {
//#debug debug
logger.debug("Show notify has been called, screen will be updated again");
if (imageCollector != null) {
imageCollector.resume();
imageCollector.newDataReady();
}
}
public Vector getRoute() {
return route;
}
public void actionCompleted() {
boolean reAddCommands = true;
if (reAddCommands && Configuration.getCfgBitState(Configuration.CFGBIT_FULLSCREEN)) {
addAllCommands();
}
}
public void stopShowingSplitScreen() {
showingSplitSetup = false;
showingTraceIconMenu = false;
showingSplitSearch = false;
showingSplitCMS = false;
refreshWindowLayout();
}
public void showIconMenu() {
if (traceIconMenu == null) {
traceIconMenu = new TraceIconMenu(this, this);
}
if (Configuration.getCfgBitState(Configuration.CFGBIT_ICONMENUS_SPLITSCREEN)
&& hasPointerEvents()) {
showingTraceIconMenu = true;
refreshWindowLayout();
traceIconMenu.sizeChanged(rootWindow.getMaxX(), rootWindow.getMaxY());
} else {
traceIconMenu.show();
}
}
/** uncache the icon menu to reflect changes in the setup or save memory */
public static void uncacheIconMenu() {
//#mdebug trace
if (traceIconMenu != null) {
logger.trace("uncaching TraceIconMenu");
}
//#enddebug
traceIconMenu = null;
}
/** interface for IconMenuWithPages: recreate the icon menu from scratch and show it (introduced for reflecting size change of the Canvas) */
public void recreateAndShowIconMenu() {
uncacheIconMenu();
showIconMenu();
}
/** interface for received actions from the IconMenu GUI */
public void performIconAction(int actionId, String choiceName) {
System.out.println("choiceName: " + choiceName);
//if (!isShowingSplitScreen()) {
if (!(isShowingSplitIconMenu() || isShowingSplitSearch()
|| isShowingSplitCMS())) {
show();
}
updateLastUserActionTime();
// when we are low on memory or during route calculation do not cache the icon menu (including scaled images)
if (routeCalc || ShareNav.getInstance().needsFreeingMemory()) {
//#debug info
logger.info("low mem: Uncaching traceIconMenu");
uncacheIconMenu();
}
if (actionId == IconActionPerformer.BACK_ACTIONID) {
if (Configuration.getCfgBitState(Configuration.CFGBIT_ICONMENUS_SPLITSCREEN)) {
stopShowingSplitScreen();
}
}
if (actionId == SAVE_PREDEF_WAYP_CMD && gpx != null && choiceName != null) {
if (gpx.isLoadingWaypoints()) {
showAlertLoadingWpt();
} else {
PositionMark posMark = getPosMark();
if (choiceName.indexOf("%s") != -1) {
mForm = new GuiWaypointPredefinedForm(this, this);
mForm.setData(choiceName, choiceName, TextField.ANY, posMark);
mForm.show();
} else if (choiceName.indexOf("%f") != -1) {
mForm = new GuiWaypointPredefinedForm(this, this);
mForm.setData(choiceName, choiceName, TextField.DECIMAL, posMark);
mForm.show();
} else {
posMark.displayName = choiceName;
}
gpx.addWayPt(posMark);
newDataReady();
}
return;
} else if (actionId == ROUTE_TO_FAVORITE_CMD && gpx != null && choiceName != null) {
// set destination from choiceName
choiceName += "*";
Vector wpt = gpx.listWayPoints();
PositionMark[] wayPts = new PositionMark[wpt.size()];
wpt.copyInto(wayPts);
PositionMark result = null;
for (int i = 0; i < wayPts.length; i++ ) {
if (choiceName.equals(wayPts[i].displayName)) {
result = wayPts[i];
}
}
if (result != null) {
RoutePositionMark pm1 = new RoutePositionMark(result.lat, result.lon);
setDestination(pm1);
commandAction(ROUTING_START_CMD);
}
} else if (actionId != IconActionPerformer.BACK_ACTIONID) {
commandAction(actionId);
}
}
/** convert distance to string based on user preferences */
// The default way to show a distance is "10km" for
// distances 10km or more, "2,6km" for distances under 10, and
// "600m" for distances under 1km.
public static String showDistance(int meters) {
return showDistance(meters, DISTANCE_GENERIC);
}
public static String showDistance(int meters, int type) {
if (Configuration.getCfgBitState(Configuration.CFGBIT_METRIC)) {
if (type == DISTANCE_UNKNOWN) {
return "???" + Locale.get("guitacho.m");
}
int MajorUnit = meters / 1000;
int MinorUnit = meters % 1000;
// km.mm
//String MinorShort = (MinorUnit / 10 < 10 ? "0" : "") + (MinorUnit / 10);
// km.m
String MinorShort = Integer.toString((int)MinorUnit / 100);
if (Configuration.getCfgBitState(Configuration.CFGBIT_DISTANCE_VIEW) && (type != DISTANCE_ALTITUDE)) {
//if (MajorUnit >= 10) {
// return Integer.toString(MajorUnit) + Locale.get("guitacho.km");
//} else
if (MajorUnit == 0) {
return Integer.toString(MinorUnit) + Locale.get("guitacho.m");
} else {
// FIXME use e.g. getDecimalSeparator() for decimal comma/point selection
return Integer.toString(MajorUnit) + "." + MinorShort + Locale.get("guitacho.km");
}
} else {
return Integer.toString(meters) + Locale.get("guitacho.m");
}
} else {
if (type == DISTANCE_UNKNOWN) {
return "???" + Locale.get("guitacho.yd");
}
int MajorUnit = (int)(meters / 1609.344f);
int MinorUnit = (int)(meters % 1609.344f + 0.5f);
// mi.dd
//String MinorShort = (MinorUnit / 16.09344f < 10.0f ? "0" : "") + (int)(MinorUnit / 16.09344f);
// mi.d
String MinorShort = Integer.toString((int)(MinorUnit / 160.9344f));
if (Configuration.getCfgBitState(Configuration.CFGBIT_DISTANCE_VIEW) && (type != DISTANCE_ALTITUDE)) {
//if (MajorUnit >= 10) {
// return Integer.toString(MajorUnit) + Locale.get("guitacho.mi");
//} else
if (MajorUnit == 0) {
return Integer.toString(MinorUnit) + Locale.get("guitacho.yd");
} else {
return Integer.toString(MajorUnit) + "." + MinorShort + Locale.get("guitacho.mi");
}
} else {
return Integer.toString((int)(meters / 0.9144 + 0.5f)) + Locale.get("guitacho.yd");
}
}
}
// FIXME: get and remember coordinates to keep track of distance
// to alert POI. Handle multiple alert POIs.
public void setNodeAlert(int type) {
if (!nodeAlert) {
nodeAlert = true;
alertNodeType = type;
}
}
//#if polish.api.finland
public void setCameraAlert(int type) {
if (!cameraAlert) {
cameraAlert = true;
}
}
//#endif
public void resetClickableMarkers() {
clickableMarkers = new Vector();
}
//#if polish.android
// FIXME should rather start a background service for recording track or
// other fuctionality which requires staying active
public void getPersistent() {
//if (Configuration.getCfgBitState(Configuration.CFGBIT_BACKLIGHT_ANDROID_WAKELOCK)
// && Configuration.getCfgBitState(Configuration.CFGBIT_BACKLIGHT_ON)) {
// // wakelock already active
// return;
//}
if (pm == null) {
pm = (PowerManager) MidletBridge.instance.getSystemService(Context.POWER_SERVICE);
}
if (wl == null) {
wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "ShareNavRecord");
wl.acquire();
}
}
public void leavePersistent() {
if (wl != null) {
wl.release();
wl = null;
}
}
//#endif
public void setRoutePointCenter(IntPoint p, PaintContext pc) {
// System.out.println("setRoutePointCenter() called");
if (p != null && Configuration.getCfgBitState(Configuration.CFGBIT_KEEP_ON_ROAD_IN_ROUTE_GUIDANCE) && RouteLineProducer.isRouteLineProduced()) {
Node routePointNode = new Node();
if (pc != null) {
pc.getP().inverse(p.x, p.y, routePointNode);
routePointCenter = routePointNode.copy();
}
}
}
public void addClickableMarker(int x, int y, int urlIdx, int phoneIdx, int nodeID) {
ClickableCoords coords = new ClickableCoords();
coords.x = x - imageCollector.xScreenOverscan;
coords.y = y - imageCollector.yScreenOverscan;
coords.urlIdx = urlIdx;
coords.phoneIdx = phoneIdx;
coords.nodeID = nodeID;
clickableMarkers.addElement(coords);
}
public boolean internetAccessAllowed() {
if (Configuration.getCfgBitState(Configuration.CFGBIT_INTERNET_ACCESS)) {
return true;
}
ShareNav.getInstance().alert("ShareNav", Locale.get("trace.OnlineFeaturesDisabledIn") + " " + Locale.get("traceiconmenu.Setup") + " / " + Locale.get("guidiscovericonmenu.Online"), 5000); // Online features are disabled in Setup / Online
return false;
}
}